如何在 freetype 中使用 FT_RENDER_MODE_SDF?

1 c freetype font-rendering

我对字体渲染很陌生,我正在尝试使用 freetype 生成有符号距离场,以便它可以在 OpenGL 中的片段着色器中使用。这是我尝试过的代码:

           error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
           if (error)
           {
              // Handle error
           }
 
           
           error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF);
           if (error)
           {
              // Handle error
           }

Run Code Online (Sandbox Code Playgroud)

也许我完全误解了 SDF 的想法,但我的想法是我可以给 freetype 一个 ttf 文件,并使用 FT_RENDER_MODE_SDF 它应该生成一个带有符号距离的缓冲区。但 FT_Render_Glyph 返回错误 (19),恰好是“无法渲染此字形格式”。

小智 8

SDF 支持于 2020 年底添加,并于 2021 年下半年添加新模块,因此请确保您拥有比该版本更新的版本。例如,2.6 早于 2.12.0(撰写本文时的最新版本)。

好了,让我们开始吧。

我假设您已经完成 LearnOpenGL 的字体渲染教程,并且可以成功在屏幕上渲染文本。你应该有这样的东西(注意新添加的内容):

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction

FT_GlyphSlot slot = face->glyph; // <-- This is new

for (unsigned char c = 0; c < 128; c++)
{
    // Load character glyph 
    if (FT_Load_Char(face, c, FT_LOAD_RENDER))
    {
        // error message
        continue;
    }

    FT_Render_Glyph(slot, FT_RENDER_MODE_SDF); // <-- And this is new

    // Generate texture
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D( ... );
    ...
}
Run Code Online (Sandbox Code Playgroud)

渲染文本时,你必须告诉 OpenGL 不要将四边形的片段写入深度缓冲区,否则相邻的字形将重叠并开始闪烁:

glDepthMask(GL_FALSE); // Don't write into the depth buffer
RenderText(pTextShader, text, 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
glDepthMask(GL_TRUE); // Re-enable writing to the depth buffer
Run Code Online (Sandbox Code Playgroud)

如果您想将文本作为对象放置在场景中的世界空间中,那么在顶点着色器中您可以使用:

gl_Position = uVp * uModel * vec4(vertex.xy, 0.0, 1.0); // uVp is "projection * view" on the CPU side
Run Code Online (Sandbox Code Playgroud)

但是,这有点超出了您的问题范围。通过将相机围绕文本旋转,可以更轻松地从各个角度检查文本。确保glDisable(GL_CULL_FACE)在绘制字形之前运行,以禁用背面剔除,以便它们从两侧都可见。

至于片段着色器我建议你看这个视频

最低限度是:

void main()
{    
    float glyphShape = texture(uGlyphTexture, TexCoords).r;

    if (glyphShape < 0.5)
        discard;

    oFragColor = vec4(uTextColor, 1.0);
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

我认为他们之间有很大的区别,你不觉得吗?

玩得开心!