How to use FT_RENDER_MODE_SDF in freetype?

3.4k Views Asked by At

I'm quite new to font rendering and I'm trying to generate signed distance field with freetype so that it can be used in fragment shader in OpenGL. Here is the code that I tried:

           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
           }

Maybe I completly misunderstand the idea of SDF, but my thought was that I could give freetype a ttf file and with FT_RENDER_MODE_SDF it should produce a buffer with signed distances. But FT_Render_Glyph returns an error (19) which happens to be "cannot render this glyph format".

1

There are 1 best solutions below

3
Andrei Despinoiu On BEST ANSWER

SDF support was added at the end of 2020, with a new module in the second half of 2021, so make sure you have a more recent version than that. For example, 2.6 is older than 2.12.0 (the newest at the time of writing).

With that out of the way, let's get started.

I'm assuming you've completed the font rendering tutorial from LearnOpenGL and you can successfully render text on the screen. You should have something like this (notice the new additions):

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( ... );
    ...
}

When rendering the text, you have to tell OpenGL not to write the fragments of the quads to the depth buffer, otherwise adjacent glyphs will overlap and start to flicker:

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

If you want to place the text as an object in your scene, in world-space, then in the vertex shader you can use:

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

However, this is a bit outside the scope of your question. It just makes it easier to inspect the text from all angles by circling the camera around it. Make sure you run glDisable(GL_CULL_FACE) before drawing the glyphs, to disable backface culling, so they're visible from both sides.

As for the fragment shader I suggest you watch this video.

The bare minimum would be:

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

    if (glyphShape < 0.5)
        discard;

    oFragColor = vec4(uTextColor, 1.0);
}

Result:

enter image description here

I think there's a pretty stark difference between them, wouldn't you say?

Have fun!