diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h index 5b70fb5..67869f1 100644 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h +++ b/Extra2D/include/extra2d/graphics/backends/opengl/gl_renderer.h @@ -134,6 +134,7 @@ private: IWindow* window_; GLSpriteBatch spriteBatch_; Ptr shapeShader_; + Ptr sdfFontShader_; // SDF字体专用着色器 GLuint shapeVao_; // 形状 VAO(手动管理,用于顶点属性配置) GLBuffer shapeBuffer_; // 形状 VBO(使用 GLBuffer 管理) diff --git a/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h b/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h index a3593ea..e2b5650 100644 --- a/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h +++ b/Extra2D/include/extra2d/graphics/backends/opengl/gl_sprite_batch.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,9 @@ public: void begin(const glm::mat4 &viewProjection); void end(); + // 使用指定着色器开始批处理 + void begin(const glm::mat4 &viewProjection, Ptr shader); + // 绘制单个精灵 void draw(const Texture &texture, const SpriteData &data); @@ -37,6 +41,16 @@ public: // 获取绘制调用次数 uint32_t getDrawCallCount() const { return drawCallCount_; } + // 设置自定义着色器(用于SDF字体等特殊渲染) + void setShader(Ptr shader); + + // 获取当前着色器 + Ptr getShader() const { return shader_; } + + // 设置额外的uniform值(用于SDF字体等特殊渲染) + void setExtraUniforms(const UniformValueMap &uniforms) { extraUniforms_ = uniforms; } + void clearExtraUniforms() { extraUniforms_.clear(); } + private: // OpenGL 对象 GLuint vao_; @@ -60,6 +74,9 @@ private: uint32_t drawCallCount_; glm::mat4 viewProjection_; + // 额外的uniform值(用于SDF字体等特殊渲染) + UniformValueMap extraUniforms_; + // 内部方法 void flush(); void submitBatch(); diff --git a/Extra2D/include/extra2d/graphics/shader/shader_loader.h b/Extra2D/include/extra2d/graphics/shader/shader_loader.h index b752cd6..4e7e826 100644 --- a/Extra2D/include/extra2d/graphics/shader/shader_loader.h +++ b/Extra2D/include/extra2d/graphics/shader/shader_loader.h @@ -27,6 +27,8 @@ struct ShaderUniformDef { std::string type; std::string description; float defaultValue = 0.0f; // 默认值(用于float类型) + float defaultVec2[2] = {0, 0}; // 默认值(用于vec2类型) + float defaultVec3[3] = {0, 0, 0}; // 默认值(用于vec3类型) float defaultVec4[4] = {0, 0, 0, 0}; // 默认值(用于vec4类型) float defaultMat4[16] = { 1, 0, 0, 0, 0, 1, 0, 0, diff --git a/Extra2D/shaders/backends/opengl/builtin/sdf_font.frag b/Extra2D/shaders/backends/opengl/builtin/sdf_font.frag new file mode 100644 index 0000000..2bfb3d2 --- /dev/null +++ b/Extra2D/shaders/backends/opengl/builtin/sdf_font.frag @@ -0,0 +1,39 @@ +#version 300 es +precision highp float; + +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_opacity; +uniform float u_sdfThreshold; +uniform float u_sdfSmoothness; +uniform vec2 u_textureSize; + +out vec4 fragColor; + +void main() { + // 采样 SDF 纹理(SDF 值存储在 alpha 通道,范围 0-255 已映射到 0-1) + float sdfValue = texture(u_texture, v_texCoord).a; + + // 使用 fwidth 计算屏幕空间的变化率 + float fw = fwidth(sdfValue); + + // 平衡的抗锯齿:根据屏幕空间变化率调整平滑范围 + // 在放大时更平滑,缩小时更锐利 + float smoothRange = max(u_sdfSmoothness, fw * 0.5); + + // 使用 smoothstep 进行抗锯齿 + float alpha = smoothstep(u_sdfThreshold - smoothRange, + u_sdfThreshold + smoothRange, + sdfValue); + + // 应用颜色和透明度 + fragColor = v_color; + fragColor.a *= alpha * u_opacity; + + // 丢弃完全透明的像素 + if (fragColor.a < 0.001) { + discard; + } +} diff --git a/Extra2D/shaders/shared/builtin/sdf_font.json b/Extra2D/shaders/shared/builtin/sdf_font.json new file mode 100644 index 0000000..56586bb --- /dev/null +++ b/Extra2D/shaders/shared/builtin/sdf_font.json @@ -0,0 +1,60 @@ +{ + "name": "sdf_font", + "category": "builtin", + "version": "1.0", + "description": "SDF字体渲染Shader,支持高质量抗锯齿", + "uniforms": { + "u_viewProjection": { + "type": "mat4", + "default": [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ], + "description": "视图投影矩阵" + }, + "u_model": { + "type": "mat4", + "default": [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ], + "description": "模型矩阵" + }, + "u_opacity": { + "type": "float", + "default": 1.0, + "description": "透明度" + }, + "u_sdfThreshold": { + "type": "float", + "default": 0.5, + "description": "SDF阈值,默认0.5,范围0-1。值越小字形越粗,越大越细" + }, + "u_sdfSmoothness": { + "type": "float", + "default": 0.02, + "description": "SDF平滑度,控制抗锯齿范围,越小越锐利,默认0.02" + }, + "u_textureSize": { + "type": "vec2", + "default": [512, 512], + "description": "SDF纹理尺寸,用于计算像素级平滑" + } + }, + "samplers": { + "u_texture": { + "binding": 0, + "description": "SDF纹理采样器" + } + }, + "backends": { + "opengl": { + "vertex": "backends/opengl/builtin/sprite.vert", + "fragment": "backends/opengl/builtin/sdf_font.frag" + } + } +} diff --git a/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp b/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp index fd0e89c..3a17c24 100644 --- a/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_font_atlas.cpp @@ -165,6 +165,7 @@ void GLFontAtlas::createAtlas() { std::vector emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0); texture_ = std::make_unique(ATLAS_WIDTH, ATLAS_HEIGHT, emptyData.data(), channels); + // 所有字体都使用线性过滤,SDF的抗锯齿由着色器处理 texture_->setFilter(true); // 初始化矩形打包上下文 - 持久化以支持增量打包 diff --git a/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp b/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp index 4067808..f4ded47 100644 --- a/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp @@ -654,18 +654,42 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, float cursorY = y; float baselineY = cursorY + font.getAscent(); - // 确保批处理已激活(自动批处理) - if (autoBatchEnabled_) { - ensureBatchActive(); - } + // 检查是否为SDF字体 + bool isSDF = font.isSDF(); - // 检查纹理变化,如果纹理不同则先提交当前批次 - if (autoBatchEnabled_ && currentBatchTexture_ != nullptr && - currentBatchTexture_ != font.getTexture()) { - submitPendingSprites(); - } - if (autoBatchEnabled_) { - currentBatchTexture_ = font.getTexture(); + // 如果是SDF字体,切换到SDF着色器 + if (isSDF && sdfFontShader_) { + // 先提交当前普通批处理 + if (autoBatchEnabled_ && !pendingSprites_.empty()) { + submitPendingSprites(); + } + + // 使用SDF着色器开始新的批处理 + // 设置需要动态计算的uniform值 + UniformValueMap sdfUniformValues; + sdfUniformValues["u_viewProjection"] = viewProjection_; + if (font.getTexture()) { + sdfUniformValues["u_textureSize"] = ShaderUniformValue( + glm::vec2(static_cast(font.getTexture()->getWidth()), + static_cast(font.getTexture()->getHeight()))); + } + // 设置额外的uniform值,让submitBatch使用 + spriteBatch_.setExtraUniforms(sdfUniformValues); + spriteBatch_.begin(viewProjection_, sdfFontShader_); + } else { + // 确保批处理已激活(自动批处理) + if (autoBatchEnabled_) { + ensureBatchActive(); + } + + // 检查纹理变化,如果纹理不同则先提交当前批次 + if (autoBatchEnabled_ && currentBatchTexture_ != nullptr && + currentBatchTexture_ != font.getTexture()) { + submitPendingSprites(); + } + if (autoBatchEnabled_) { + currentBatchTexture_ = font.getTexture(); + } } // 收集所有字符数据用于批处理 @@ -677,7 +701,10 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, if (codepoint == '\n') { // 换行时,将当前行添加到待处理列表 if (!sprites.empty()) { - if (autoBatchEnabled_) { + if (isSDF && sdfFontShader_) { + // SDF模式直接提交 + spriteBatch_.drawBatch(*font.getTexture(), sprites); + } else if (autoBatchEnabled_) { pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), sprites.end()); } else { @@ -724,7 +751,7 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, sprites.push_back(data); // 自动批处理:如果缓冲区满,先提交当前批次 - if (autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) { + if (!isSDF && autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) { pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), sprites.end()); sprites.clear(); @@ -734,7 +761,10 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, // 提交剩余的字符 if (!sprites.empty()) { - if (autoBatchEnabled_) { + if (isSDF && sdfFontShader_) { + // SDF模式直接提交 + spriteBatch_.drawBatch(*font.getTexture(), sprites); + } else if (autoBatchEnabled_) { pendingSprites_.insert(pendingSprites_.end(), sprites.begin(), sprites.end()); } else { @@ -742,6 +772,18 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text, spriteBatch_.drawBatch(*font.getTexture(), sprites); } } + + // 如果是SDF字体,结束批处理并恢复普通着色器 + if (isSDF && sdfFontShader_) { + spriteBatch_.end(); + // 清除额外的uniform值 + spriteBatch_.clearExtraUniforms(); + // 恢复默认的sprite着色器 + auto defaultShader = ShaderManager::getInstance().getBuiltin("sprite"); + if (defaultShader) { + spriteBatch_.setShader(defaultShader); + } + } } /** @@ -762,6 +804,14 @@ void GLRenderer::initShapeRendering() { } } + // 加载SDF字体着色器 + sdfFontShader_ = ShaderManager::getInstance().getBuiltin("sdf_font"); + if (!sdfFontShader_) { + E2D_LOG_WARN("获取SDF字体着色器失败,SDF字体将使用普通渲染"); + } else { + E2D_LOG_INFO("SDF字体着色器加载成功"); + } + // 初始化形状 GLBuffer(使用 Dynamic 使用模式,因为每帧都会更新) BufferDesc shapeBufferDesc; shapeBufferDesc.type = BufferType::Vertex; @@ -889,14 +939,15 @@ void GLRenderer::flushShapeBatch() { if (shapeShader_) { shapeShader_->bind(); - + // 只提供需要动态计算的值,其他值使用JSON中定义的默认值 UniformValueMap uniformValues; uniformValues["u_viewProjection"] = viewProjection_; - + // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) // 使用着色器自己的名称(从JSON中解析的name字段) - ShaderManager::getInstance().applyUniforms(shapeShader_, shapeShader_->getName(), uniformValues); + ShaderManager::getInstance().applyUniforms( + shapeShader_, shapeShader_->getName(), uniformValues); } // 使用 GLBuffer::updateData() 更新缓冲区数据 @@ -925,14 +976,15 @@ void GLRenderer::flushLineBatch() { glLineWidth(currentLineWidth_); if (shapeShader_) { shapeShader_->bind(); - + // 只提供需要动态计算的值,其他值使用JSON中定义的默认值 UniformValueMap uniformValues; uniformValues["u_viewProjection"] = viewProjection_; - + // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) // 使用着色器自己的名称(从JSON中解析的name字段) - ShaderManager::getInstance().applyUniforms(shapeShader_, shapeShader_->getName(), uniformValues); + ShaderManager::getInstance().applyUniforms( + shapeShader_, shapeShader_->getName(), uniformValues); } // 使用 GLBuffer::updateData() 更新缓冲区数据 diff --git a/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp b/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp index 6226326..fb48994 100644 --- a/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp +++ b/Extra2D/src/graphics/backends/opengl/gl_sprite_batch.cpp @@ -93,6 +93,22 @@ void GLSpriteBatch::begin(const glm::mat4 &viewProjection) { ebo_.bind(); } +void GLSpriteBatch::begin(const glm::mat4 &viewProjection, Ptr shader) { + // 设置自定义着色器 + if (shader) { + shader_ = shader; + } + begin(viewProjection); +} + +void GLSpriteBatch::setShader(Ptr shader) { + // 如果当前有未提交的批次,先提交 + if (batch_.getSpriteCount() > 0) { + flush(); + } + shader_ = shader; +} + void GLSpriteBatch::end() { if (batch_.getSpriteCount() > 0) { flush(); @@ -166,6 +182,11 @@ void GLSpriteBatch::submitBatch() { UniformValueMap uniformValues; uniformValues["u_viewProjection"] = viewProjection_; + // 合并额外的uniform值(如SDF字体的u_textureSize) + for (const auto& [name, value] : extraUniforms_) { + uniformValues[name] = value; + } + // 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值) // 使用着色器自己的名称(从JSON中解析的name字段) ShaderManager::getInstance().applyUniforms(shader_, shader_->getName(), diff --git a/Extra2D/src/graphics/shader/shader_manager.cpp b/Extra2D/src/graphics/shader/shader_manager.cpp index 83cc2a1..01fcf55 100644 --- a/Extra2D/src/graphics/shader/shader_manager.cpp +++ b/Extra2D/src/graphics/shader/shader_manager.cpp @@ -549,6 +549,16 @@ Ptr ShaderManager::loadFromMetadata(const std::string &jsonPath, def.defaultInt = value["default"].get(); } else if (def.type == "bool") { def.defaultBool = value["default"].get(); + } else if (def.type == "vec2" && value["default"].is_array()) { + auto arr = value["default"].get>(); + for (size_t i = 0; i < arr.size() && i < 2; ++i) { + def.defaultVec2[i] = arr[i]; + } + } else if (def.type == "vec3" && value["default"].is_array()) { + auto arr = value["default"].get>(); + for (size_t i = 0; i < arr.size() && i < 3; ++i) { + def.defaultVec3[i] = arr[i]; + } } else if (def.type == "vec4" && value["default"].is_array()) { auto arr = value["default"].get>(); for (size_t i = 0; i < arr.size() && i < 4; ++i) { @@ -806,6 +816,12 @@ void ShaderManager::applyUniforms(Ptr shader, shader->setInt(name, def.defaultInt); } else if (def.type == "bool") { shader->setBool(name, def.defaultBool); + } else if (def.type == "vec2") { + shader->setVec2(name, + glm::vec2(def.defaultVec2[0], def.defaultVec2[1])); + } else if (def.type == "vec3") { + shader->setVec3(name, glm::vec3(def.defaultVec3[0], def.defaultVec3[1], + def.defaultVec3[2])); } else if (def.type == "vec4") { shader->setVec4(name, glm::vec4(def.defaultVec4[0], def.defaultVec4[1], diff --git a/examples/text_rendering/assets/fonts/arial.ttf b/examples/text_rendering/assets/fonts/arial.ttf deleted file mode 100644 index 8682d94..0000000 Binary files a/examples/text_rendering/assets/fonts/arial.ttf and /dev/null differ diff --git a/examples/text_rendering/assets/fonts/fonts.ttf b/examples/text_rendering/assets/fonts/fonts.ttf new file mode 100644 index 0000000..211f4ff Binary files /dev/null and b/examples/text_rendering/assets/fonts/fonts.ttf differ diff --git a/examples/text_rendering/main.cpp b/examples/text_rendering/main.cpp index b7c54f4..c3017d5 100644 --- a/examples/text_rendering/main.cpp +++ b/examples/text_rendering/main.cpp @@ -23,17 +23,10 @@ public: } try { - font_ = renderer_->createFontAtlas("assets/fonts/arial.ttf", 24); + font_ = renderer_->createFontAtlas("assets/fonts/fonts.ttf", 26, true); } catch (...) { - std::cerr << "Failed to load font from assets/fonts/arial.ttf" + std::cerr << "Failed to load font from assets/fonts/fonts.ttf" << std::endl; - // 尝试使用备用路径 - try { - font_ = renderer_->createFontAtlas("C:/Windows/Fonts/arial.ttf", 24); - } catch (...) { - std::cerr << "Failed to load font from system path!" << std::endl; - font_ = nullptr; - } } if (font_) {