diff --git a/Extra2D/include/extra2d/render/shape_renderer.h b/Extra2D/include/extra2d/render/shape_renderer.h index c391b61..3def83f 100644 --- a/Extra2D/include/extra2d/render/shape_renderer.h +++ b/Extra2D/include/extra2d/render/shape_renderer.h @@ -1,8 +1,8 @@ #pragma once -#include -#include #include +#include +#include #include #include #include @@ -23,7 +23,8 @@ struct ShapeVertex { /** * @brief 图形渲染器 * - * 支持矩形、圆形、线条等基本图形的批量渲染 + * 支持矩形、圆形、线条等基本图形的批量渲染。 + * 必须设置 Material 才能渲染。 */ class ShapeRenderer { public: @@ -43,6 +44,18 @@ public: */ void shutdown(); + /** + * @brief 设置材质(必须设置才能渲染) + * @param material 材质模板 + */ + void setMaterial(Ref material) { material_ = material; } + + /** + * @brief 获取材质 + * @return 材质模板 + */ + Ref material() const { return material_; } + /** * @brief 开始批量渲染 */ @@ -104,14 +117,11 @@ private: Array vertices_; size_t vertexCount_ = 0; - GLuint fillShader_ = 0; - GLuint lineShader_ = 0; glm::mat4 viewProjection_; - - uint32 drawCalls_ = 0; + Ref material_; float currentLineWidth_ = 1.0f; + uint32 drawCalls_ = 0; - bool createShaders(); void flush(GLenum mode); void addVertex(float x, float y, const glm::vec4& color); }; diff --git a/Extra2D/include/extra2d/render/sprite_renderer.h b/Extra2D/include/extra2d/render/sprite_renderer.h index f5f0038..1428a06 100644 --- a/Extra2D/include/extra2d/render/sprite_renderer.h +++ b/Extra2D/include/extra2d/render/sprite_renderer.h @@ -40,7 +40,8 @@ struct SpriteData { /** * @brief 精灵渲染器 * - * 高性能批量精灵渲染器,支持 Material 系统 + * 高性能批量精灵渲染器,支持 Material 系统。 + * 必须设置 Material 才能渲染。 */ class SpriteRenderer { public: @@ -64,7 +65,7 @@ public: void shutdown(); /** - * @brief 设置材质 + * @brief 设置材质(必须设置才能渲染) * @param material 材质模板 */ void setMaterial(Ref material) { material_ = material; } @@ -80,20 +81,14 @@ public: */ void begin(const glm::mat4 &viewProjection); - /** - * @brief 绘制精灵(使用默认材质) - * @param texture 纹理 ID - * @param data 精灵数据 - */ - void draw(GLuint texture, const SpriteData &data); - /** * @brief 绘制精灵(使用材质实例) * @param texture 纹理 ID * @param data 精灵数据 - * @param instance 材质实例 + * @param instance 材质实例(nullptr 则使用默认材质) */ - void draw(GLuint texture, const SpriteData &data, MaterialInstance *instance); + void draw(GLuint texture, const SpriteData &data, + MaterialInstance *instance = nullptr); /** * @brief 绘制精灵(使用属性块) @@ -123,7 +118,6 @@ private: Array vertices_; size_t vertexCount_ = 0; - GLuint shader_ = 0; GLuint currentTexture_ = 0; glm::mat4 viewProjection_; @@ -133,9 +127,7 @@ private: uint32 drawCalls_ = 0; uint32 spriteCount_ = 0; - bool createShader(); void flush(); - void flushWithMaterial(); void addQuad(const SpriteData &data, GLuint texture); }; diff --git a/Extra2D/shader/msdf_font/msdf_font.frag b/Extra2D/shader/msdf_font/msdf_font.frag new file mode 100644 index 0000000..6f48a7f --- /dev/null +++ b/Extra2D/shader/msdf_font/msdf_font.frag @@ -0,0 +1,54 @@ +#version 450 core +in vec2 v_texCoord; + +uniform sampler2D u_texture; +uniform float u_pxRange; +uniform vec4 u_color; +uniform float u_outlineWidth; +uniform vec4 u_outlineColor; +uniform vec2 u_shadowOffset; +uniform vec4 u_shadowColor; + +out vec4 fragColor; + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +void main() { + vec3 msdf = texture(u_texture, v_texCoord).rgb; + float sigDist = median(msdf.r, msdf.g, msdf.b); + + float pxRange = u_pxRange; + float alpha = smoothstep(0.5 - 0.5/pxRange, 0.5 + 0.5/pxRange, sigDist); + + float outlineAlpha = 0.0; + if (u_outlineWidth > 0.0) { + outlineAlpha = smoothstep( + 0.5 - u_outlineWidth - 0.5/pxRange, + 0.5 - u_outlineWidth + 0.5/pxRange, + sigDist + ); + } + + float shadowAlpha = 0.0; + if (u_shadowOffset != vec2(0.0)) { + vec3 shadowMsdf = texture(u_texture, v_texCoord - u_shadowOffset).rgb; + float shadowDist = median(shadowMsdf.r, shadowMsdf.g, shadowMsdf.b); + shadowAlpha = smoothstep(0.5 - 0.5/pxRange, 0.5 + 0.5/pxRange, shadowDist); + } + + vec4 color = vec4(0.0); + + if (shadowAlpha > 0.0) { + color = mix(color, u_shadowColor, shadowAlpha * u_shadowColor.a); + } + + if (outlineAlpha > 0.0) { + color = mix(color, u_outlineColor, outlineAlpha * u_outlineColor.a); + } + + color = mix(color, u_color, alpha * u_color.a); + + fragColor = color; +} diff --git a/Extra2D/shader/msdf_font/msdf_font.json b/Extra2D/shader/msdf_font/msdf_font.json new file mode 100644 index 0000000..cd1d0fa --- /dev/null +++ b/Extra2D/shader/msdf_font/msdf_font.json @@ -0,0 +1,46 @@ +{ + "name": "msdf_font", + "vertex": "msdf_font.vert", + "fragment": "msdf_font.frag", + "properties": { + "u_texture": { + "type": "sampler2D", + "default": 0 + }, + "u_color": { + "type": "vec4", + "default": [1.0, 1.0, 1.0, 1.0] + }, + "u_pxRange": { + "type": "float", + "default": 4.0 + }, + "u_outlineWidth": { + "type": "float", + "default": 0.0 + }, + "u_outlineColor": { + "type": "vec4", + "default": [0.0, 0.0, 0.0, 1.0] + }, + "u_shadowOffset": { + "type": "vec2", + "default": [0.0, 0.0] + }, + "u_shadowColor": { + "type": "vec4", + "default": [0.0, 0.0, 0.0, 0.5] + } + }, + "states": { + "blend": { + "enabled": true, + "src": "SRC_ALPHA", + "dst": "ONE_MINUS_SRC_ALPHA" + }, + "depth": { + "enabled": false, + "write": false + } + } +} diff --git a/Extra2D/shader/msdf_font/msdf_font.vert b/Extra2D/shader/msdf_font/msdf_font.vert new file mode 100644 index 0000000..c00348b --- /dev/null +++ b/Extra2D/shader/msdf_font/msdf_font.vert @@ -0,0 +1,12 @@ +#version 450 core +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; + +uniform mat4 u_projection; + +out vec2 v_texCoord; + +void main() { + gl_Position = u_projection * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; +} diff --git a/Extra2D/shader/shape/shape.frag b/Extra2D/shader/shape/shape.frag new file mode 100644 index 0000000..65a788c --- /dev/null +++ b/Extra2D/shader/shape/shape.frag @@ -0,0 +1,8 @@ +#version 450 core +in vec4 v_color; + +out vec4 fragColor; + +void main() { + fragColor = v_color; +} diff --git a/Extra2D/shader/shape/shape.json b/Extra2D/shader/shape/shape.json new file mode 100644 index 0000000..8acdf6d --- /dev/null +++ b/Extra2D/shader/shape/shape.json @@ -0,0 +1,22 @@ +{ + "name": "shape_default", + "vertex": "shape.vert", + "fragment": "shape.frag", + "properties": { + "u_color": { + "type": "vec4", + "default": [1.0, 1.0, 1.0, 1.0] + } + }, + "states": { + "blend": { + "enabled": true, + "src": "SRC_ALPHA", + "dst": "ONE_MINUS_SRC_ALPHA" + }, + "depth": { + "enabled": false, + "write": false + } + } +} diff --git a/Extra2D/shader/shape/shape.vert b/Extra2D/shader/shape/shape.vert new file mode 100644 index 0000000..e6c0f9b --- /dev/null +++ b/Extra2D/shader/shape/shape.vert @@ -0,0 +1,12 @@ +#version 450 core +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec4 a_color; + +uniform mat4 u_viewProjection; + +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); + v_color = a_color; +} diff --git a/Extra2D/shader/sprite/sprite.frag b/Extra2D/shader/sprite/sprite.frag new file mode 100644 index 0000000..4f46346 --- /dev/null +++ b/Extra2D/shader/sprite/sprite.frag @@ -0,0 +1,11 @@ +#version 450 core +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; + +out vec4 fragColor; + +void main() { + fragColor = texture(u_texture, v_texCoord) * v_color; +} diff --git a/Extra2D/shader/sprite/sprite.json b/Extra2D/shader/sprite/sprite.json new file mode 100644 index 0000000..dd9cbd0 --- /dev/null +++ b/Extra2D/shader/sprite/sprite.json @@ -0,0 +1,26 @@ +{ + "name": "sprite_default", + "vertex": "sprite.vert", + "fragment": "sprite.frag", + "properties": { + "u_texture": { + "type": "sampler2D", + "default": 0 + }, + "u_color": { + "type": "vec4", + "default": [1.0, 1.0, 1.0, 1.0] + } + }, + "states": { + "blend": { + "enabled": true, + "src": "SRC_ALPHA", + "dst": "ONE_MINUS_SRC_ALPHA" + }, + "depth": { + "enabled": false, + "write": false + } + } +} diff --git a/Extra2D/shader/sprite/sprite.vert b/Extra2D/shader/sprite/sprite.vert new file mode 100644 index 0000000..406262a --- /dev/null +++ b/Extra2D/shader/sprite/sprite.vert @@ -0,0 +1,15 @@ +#version 450 core +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} diff --git a/Extra2D/shader/text/text.frag b/Extra2D/shader/text/text.frag new file mode 100644 index 0000000..74557f7 --- /dev/null +++ b/Extra2D/shader/text/text.frag @@ -0,0 +1,12 @@ +#version 450 core +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; + +out vec4 fragColor; + +void main() { + float alpha = texture(u_texture, v_texCoord).r; + fragColor = vec4(v_color.rgb, v_color.a * alpha); +} diff --git a/Extra2D/shader/text/text.json b/Extra2D/shader/text/text.json new file mode 100644 index 0000000..c156dd2 --- /dev/null +++ b/Extra2D/shader/text/text.json @@ -0,0 +1,26 @@ +{ + "name": "text_default", + "vertex": "text.vert", + "fragment": "text.frag", + "properties": { + "u_texture": { + "type": "sampler2D", + "default": 0 + }, + "u_color": { + "type": "vec4", + "default": [1.0, 1.0, 1.0, 1.0] + } + }, + "states": { + "blend": { + "enabled": true, + "src": "SRC_ALPHA", + "dst": "ONE_MINUS_SRC_ALPHA" + }, + "depth": { + "enabled": false, + "write": false + } + } +} diff --git a/Extra2D/shader/text/text.vert b/Extra2D/shader/text/text.vert new file mode 100644 index 0000000..749db5e --- /dev/null +++ b/Extra2D/shader/text/text.vert @@ -0,0 +1,15 @@ +#version 450 core +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_projection; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_projection * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} diff --git a/Extra2D/src/render/shape_renderer.cpp b/Extra2D/src/render/shape_renderer.cpp index 37aabb5..e6c37c3 100644 --- a/Extra2D/src/render/shape_renderer.cpp +++ b/Extra2D/src/render/shape_renderer.cpp @@ -5,32 +5,6 @@ namespace extra2d { -static const char* SHAPE_VERTEX_SHADER = R"( -#version 450 core -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec4 a_color; - -uniform mat4 u_viewProjection; - -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); - v_color = a_color; -} -)"; - -static const char* SHAPE_FRAGMENT_SHADER = R"( -#version 450 core -in vec4 v_color; - -out vec4 fragColor; - -void main() { - fragColor = v_color; -} -)"; - ShapeRenderer::ShapeRenderer() { vertices_.resize(MAX_VERTICES); } @@ -57,22 +31,13 @@ bool ShapeRenderer::init() { vao_->setFormat(format); vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(ShapeVertex)); - if (!createShaders()) return false; - return true; } void ShapeRenderer::shutdown() { - if (fillShader_) { - glDeleteProgram(fillShader_); - fillShader_ = 0; - } - if (lineShader_) { - glDeleteProgram(lineShader_); - lineShader_ = 0; - } vao_.reset(); vbo_.reset(); + material_.reset(); } void ShapeRenderer::begin(const glm::mat4& viewProjection) { @@ -224,52 +189,20 @@ void ShapeRenderer::end() { } } -bool ShapeRenderer::createShaders() { - GLuint vs = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vs, 1, &SHAPE_VERTEX_SHADER, nullptr); - glCompileShader(vs); - - GLint success; - glGetShaderiv(vs, GL_COMPILE_STATUS, &success); - if (!success) { - glDeleteShader(vs); - return false; - } - - GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fs, 1, &SHAPE_FRAGMENT_SHADER, nullptr); - glCompileShader(fs); - - glGetShaderiv(fs, GL_COMPILE_STATUS, &success); - if (!success) { - glDeleteShader(vs); - glDeleteShader(fs); - return false; - } - - fillShader_ = glCreateProgram(); - glAttachShader(fillShader_, vs); - glAttachShader(fillShader_, fs); - glLinkProgram(fillShader_); - - glGetProgramiv(fillShader_, GL_LINK_STATUS, &success); - - lineShader_ = fillShader_; - - glDeleteShader(vs); - glDeleteShader(fs); - - return success == GL_TRUE; -} - void ShapeRenderer::flush(GLenum mode) { - if (vertexCount_ == 0) return; + if (vertexCount_ == 0 || !material_) return; vbo_->update(0, vertexCount_ * sizeof(ShapeVertex), vertices_.data()); - glUseProgram(fillShader_); - GLint vpLoc = glGetUniformLocation(fillShader_, "u_viewProjection"); - glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); + material_->apply(); + + ShaderAsset* shader = material_->shader(); + if (shader) { + GLint vpLoc = shader->getUniformLocation("u_viewProjection"); + if (vpLoc >= 0) { + glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); + } + } vao_->bind(); glDrawArrays(mode, 0, static_cast(vertexCount_)); diff --git a/Extra2D/src/render/sprite_renderer.cpp b/Extra2D/src/render/sprite_renderer.cpp index 7fd7579..3cd3705 100644 --- a/Extra2D/src/render/sprite_renderer.cpp +++ b/Extra2D/src/render/sprite_renderer.cpp @@ -2,42 +2,9 @@ #include #include #include -#include namespace extra2d { -static const char* SPRITE_VERTEX_SHADER = R"( -#version 450 core -layout(location = 0) in vec2 a_position; -layout(location = 1) in vec2 a_texCoord; -layout(location = 2) in vec4 a_color; - -uniform mat4 u_viewProjection; - -out vec2 v_texCoord; -out vec4 v_color; - -void main() { - gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0); - v_texCoord = a_texCoord; - v_color = a_color; -} -)"; - -static const char* SPRITE_FRAGMENT_SHADER = R"( -#version 450 core -in vec2 v_texCoord; -in vec4 v_color; - -uniform sampler2D u_texture; - -out vec4 fragColor; - -void main() { - fragColor = texture(u_texture, v_texCoord) * v_color; -} -)"; - SpriteRenderer::SpriteRenderer() { vertices_.resize(MAX_VERTICES); } @@ -81,16 +48,10 @@ bool SpriteRenderer::init() { vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(SpriteVertex)); vao_->bindIndexBuffer(*ibo_); - if (!createShader()) return false; - return true; } void SpriteRenderer::shutdown() { - if (shader_) { - glDeleteProgram(shader_); - shader_ = 0; - } vao_.reset(); vbo_.reset(); ibo_.reset(); @@ -106,9 +67,13 @@ void SpriteRenderer::begin(const glm::mat4& viewProjection) { spriteCount_ = 0; } -void SpriteRenderer::draw(GLuint texture, const SpriteData& data) { - if (texture != currentTexture_ || vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) { +void SpriteRenderer::draw(GLuint texture, const SpriteData& data, MaterialInstance* instance) { + if (!material_) return; + + if (instance != currentInstance_ || texture != currentTexture_ || + vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) { flush(); + currentInstance_ = instance; currentTexture_ = texture; } @@ -116,22 +81,9 @@ void SpriteRenderer::draw(GLuint texture, const SpriteData& data) { spriteCount_++; } -void SpriteRenderer::draw(GLuint texture, const SpriteData& data, MaterialInstance* instance) { - if (material_ && instance) { - if (instance != currentInstance_ || texture != currentTexture_ || - vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) { - flushWithMaterial(); - currentInstance_ = instance; - currentTexture_ = texture; - } - addQuad(data, texture); - spriteCount_++; - } else { - draw(texture, data); - } -} - void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const MaterialPropertyBlock& block) { + if (!material_) return; + flush(); currentTexture_ = texture; addQuad(data, texture); @@ -139,119 +91,43 @@ void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const Material flush(); - if (material_ && material_->shader()) { + if (material_->shader()) { material_->shader()->bind(); block.apply(material_->shader()); } } void SpriteRenderer::end() { - if (material_ && currentInstance_) { - flushWithMaterial(); - } else { - flush(); - } -} - -bool SpriteRenderer::createShader() { - GLuint vs = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vs, 1, &SPRITE_VERTEX_SHADER, nullptr); - glCompileShader(vs); - - GLint success; - glGetShaderiv(vs, GL_COMPILE_STATUS, &success); - if (!success) { - glDeleteShader(vs); - return false; - } - - GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fs, 1, &SPRITE_FRAGMENT_SHADER, nullptr); - glCompileShader(fs); - - glGetShaderiv(fs, GL_COMPILE_STATUS, &success); - if (!success) { - glDeleteShader(vs); - glDeleteShader(fs); - return false; - } - - shader_ = glCreateProgram(); - glAttachShader(shader_, vs); - glAttachShader(shader_, fs); - glLinkProgram(shader_); - - glGetProgramiv(shader_, GL_LINK_STATUS, &success); - glDeleteShader(vs); - glDeleteShader(fs); - - return success == GL_TRUE; + flush(); } void SpriteRenderer::flush() { - if (vertexCount_ == 0) return; + if (vertexCount_ == 0 || !material_) return; vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data()); - if (material_ && material_->shader()) { - material_->shader()->bind(); - material_->shader()->applyStates(); - - GLint vpLoc = material_->shader()->getUniformLocation("u_viewProjection"); + ShaderAsset* shader = nullptr; + + if (currentInstance_) { + currentInstance_->apply(); + shader = currentInstance_->shader(); + } else { + material_->apply(); + shader = material_->shader(); + } + + if (shader) { + GLint vpLoc = shader->getUniformLocation("u_viewProjection"); if (vpLoc >= 0) { glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); } - GLint texLoc = material_->shader()->getUniformLocation("u_texture"); + GLint texLoc = shader->getUniformLocation("u_texture"); if (texLoc >= 0) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, currentTexture_); glUniform1i(texLoc, 0); } - } else { - glUseProgram(shader_); - GLint vpLoc = glGetUniformLocation(shader_, "u_viewProjection"); - glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); - - GLint texLoc = glGetUniformLocation(shader_, "u_texture"); - glUniform1i(texLoc, 0); - - glBindTextureUnit(0, currentTexture_); - } - - vao_->bind(); - glDrawElements(GL_TRIANGLES, static_cast(vertexCount_ / VERTICES_PER_SPRITE * INDICES_PER_SPRITE), - GL_UNSIGNED_SHORT, nullptr); - - E2D_RENDER_STATS().addDrawCall(static_cast(vertexCount_), - static_cast(vertexCount_ / 3)); - - drawCalls_++; - vertexCount_ = 0; -} - -void SpriteRenderer::flushWithMaterial() { - if (vertexCount_ == 0) return; - - vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data()); - - if (currentInstance_) { - currentInstance_->apply(); - - ShaderAsset* shader = currentInstance_->shader(); - if (shader) { - GLint vpLoc = shader->getUniformLocation("u_viewProjection"); - if (vpLoc >= 0) { - glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); - } - - GLint texLoc = shader->getUniformLocation("u_texture"); - if (texLoc >= 0) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, currentTexture_); - glUniform1i(texLoc, 0); - } - } } vao_->bind();