如何在 freetype 中使用 FT_RENDER_MODE_SDF?
How to use FT_RENDER_MODE_SDF in freetype?
我对字体渲染很陌生,我正在尝试使用 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
}
也许我完全误解了 SDF 的概念,但我的想法是我可以给 freetype 一个 ttf 文件,并且 FT_RENDER_MODE_SDF 它应该产生一个带符号距离的缓冲区。但是 FT_Render_Glyph returns 一个错误 (19) 恰好是“无法呈现此字形格式”。
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( ... );
...
}
渲染文本时,必须告诉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
如果您想将文本作为对象放置在场景中,在 world-space 中,那么在顶点着色器中您可以使用:
gl_Position = uVp * uModel * vec4(vertex.xy, 0.0, 1.0); // uVp is "projection * view" on the CPU side
但是,这有点超出您的问题范围。通过将相机环绕在文本周围,它只是让从各个角度检查文本变得更加容易。确保在绘制字形之前 运行 glDisable(GL_CULL_FACE)
禁用背面剔除,因此它们从两侧都可见。
关于片段着色器我建议你看一下this video。
最低限度为:
void main()
{
float glyphShape = texture(uGlyphTexture, TexCoords).r;
if (glyphShape < 0.5)
discard;
oFragColor = vec4(uTextColor, 1.0);
}
结果:
我认为它们之间有非常明显的区别,你不觉得吗?
玩得开心!
我对字体渲染很陌生,我正在尝试使用 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
}
也许我完全误解了 SDF 的概念,但我的想法是我可以给 freetype 一个 ttf 文件,并且 FT_RENDER_MODE_SDF 它应该产生一个带符号距离的缓冲区。但是 FT_Render_Glyph returns 一个错误 (19) 恰好是“无法呈现此字形格式”。
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( ... );
...
}
渲染文本时,必须告诉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
如果您想将文本作为对象放置在场景中,在 world-space 中,那么在顶点着色器中您可以使用:
gl_Position = uVp * uModel * vec4(vertex.xy, 0.0, 1.0); // uVp is "projection * view" on the CPU side
但是,这有点超出您的问题范围。通过将相机环绕在文本周围,它只是让从各个角度检查文本变得更加容易。确保在绘制字形之前 运行 glDisable(GL_CULL_FACE)
禁用背面剔除,因此它们从两侧都可见。
关于片段着色器我建议你看一下this video。
最低限度为:
void main()
{
float glyphShape = texture(uGlyphTexture, TexCoords).r;
if (glyphShape < 0.5)
discard;
oFragColor = vec4(uTextColor, 1.0);
}
结果:
我认为它们之间有非常明显的区别,你不觉得吗?
玩得开心!