cfreetypefont-rendering

How to use FT_RENDER_MODE_SDF in freetype?


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".


Solution

  • 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!