Text not rendering correctly - OpenGL using FreeTy

2019-01-28 09:22发布

Almost exact duplicate question: OpenGL font rendering using Freetype2.

I am trying to render text in my OpenGL program using FreeType2 (2.5.3), based on this tutorial: http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02, using the same structs as Bentebent did in his question (above).

I am having the same problem he was having with rectangles showing up instead of legible characters (even using the correct GL_TEXTURE0 parameter for glActiveTexture())

For example, rendering green strings "Hello" and "World" below it gives me this:

Rendering 'Hello' and 'World'

As with Bentebent, using gDebugger I can see that my textures are generated fine. I have scoured google/stackoverflow, tried different parameters for glTexImage2D, different formulas for color in my fragment shader, etc, but no luck. Been stuck for a while now. Any help is appreciated.

Structs to create texture atlas:

//DrawTestOpenGLWnd.h
struct FontCharacter
{
    float advanceX;
    float advanceY;

    float bitmapWidth;
    float bitmapHeight;

    float bitmapLeft;
    float bitmapTop;

    float uvOffsetX;
    float uvOffsetY;
};

struct FontTextureAtlas
{
    GLuint texture;
    GLint textureUniform;

    int width;
    int height;

    FontCharacter characters[128];

    FontTextureAtlas(FT_Face face, int h, GLint tUniform)
    {
        FT_Set_Pixel_Sizes(face, 0, h);
        FT_GlyphSlot glyphSlot = face->glyph;

        int roww = 0;
        int rowh = 0;
        width = 0;
        height = 0;

        memset(characters, 0, sizeof(FontCharacter));

        for (int i = 32; i < 128; i++)
        {
            if (FT_Load_Char(face, i, FT_LOAD_RENDER))
            {
                TRACE("Loading character %c failed\n", i);
                continue;
            }

            if (roww + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
            {
                width = std::max(width, roww);
                height += rowh;
                roww = 0;
                rowh = 0;
            }

            roww += glyphSlot->bitmap.width + 1;
            rowh = std::max(rowh, glyphSlot->bitmap.rows);
        }

        width = std::max(width, roww);
        height += rowh;

        glGenTextures(1, &texture);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glGenTextures failed\n");
        }

        glActiveTexture(GL_TEXTURE0);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glActiveTexture failed\n");
        }

        glBindTexture(GL_TEXTURE_2D, texture);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glBindTexture failed\n");
        }

        glUniform1i(tUniform, 0);
        textureUniform = tUniform;

        glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glTexImage2D failed\n");
        }

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glPixelStorei failed\n");
        }

        glPixelStorei(GL_PACK_ALIGNMENT, 1);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glPixelStorei failed\n");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glTexParameteri failed\n");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glTexParameteri failed\n");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glTexParameteri failed\n");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        if (glGetError() != GL_NO_ERROR){
            TRACE("glTexParameteri failed\n");
        }


        int ox = 0;
        int oy = 0;

        rowh = 0;

        for (int i = 32; i < 128; i++)
        {
            if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
                TRACE("Loading character %c failed\n", i);
                continue;
            }

            if (ox + glyphSlot->bitmap.width + 1 >= MAX_WIDTH) {
                oy += rowh;
                rowh = 0;
                ox = 0;
            }

            glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);

            if (glGetError() != GL_NO_ERROR) {
                TRACE("BORKED AGAIN\n");
            }

            characters[i].advanceX = glyphSlot->advance.x >> 6;
            characters[i].advanceY = glyphSlot->advance.y >> 6;

            characters[i].bitmapWidth = glyphSlot->bitmap.width;
            characters[i].bitmapHeight = glyphSlot->bitmap.rows;

            characters[i].bitmapLeft = glyphSlot->bitmap_left;
            characters[i].bitmapTop = glyphSlot->bitmap_top;

            characters[i].uvOffsetX = ox / (float)width;
            characters[i].uvOffsetY = oy / (float)height;

            rowh = std::max(rowh, glyphSlot->bitmap.rows);
            ox += glyphSlot->bitmap.width + 1;
        }

        TRACE("Generated a %d x %d (%d kb) texture atlas.\n", width, height, width * height / 1024);
    }

    ~FontTextureAtlas()
    {
        glDeleteTextures(1, &texture);
    }
};

InitFreeType function:

void DrawTestOpenGLWnd::InitFreeType(char * strFontFilePath)
{

    m_error = FT_Init_FreeType(&m_library);
    if (m_error) {
        TRACE("An error occurred during library initialization");
    }

    m_error = FT_New_Face(m_library, strFontFilePath, 0, &m_face);
    if (m_error == FT_Err_Unknown_File_Format) {
        TRACE("Font file could be opened and read, but it appears that its font format is unsupported");
    }
    else if (m_error) {
        TRACE("Font file could not be opened or read. Or it's broken.");
    }

    m_program_text = LoadShaders("TextVertexShader.vertexshader", "TextFragmentShader.fragmentshader");
    glUseProgram(m_program_text);

    m_uniform_texture = glGetUniformLocation(m_program_text, "texture");
    m_uniform_textColor = glGetUniformLocation(m_program_text, "textColor");

    glGenVertexArrays(1, &vao_text);
    glBindVertexArray(vao_text);

    glGenBuffers(1, &vbo_text);
    //glBindBuffer(GL_ARRAY_BUFFER, vbo_text);

    a48 = new FontTextureAtlas(m_face, 48, m_uniform_texture);
    a24 = new FontTextureAtlas(m_face, 24, m_uniform_texture);
    a12 = new FontTextureAtlas(m_face, 12, m_uniform_texture);
}

RenderText function:

void DrawTestOpenGLWnd::RenderText(char * text, FontTextureAtlas * atlas, float x, float y, float sx, float sy)
{
    glUseProgram(m_program_text);
    const unsigned char* p;

    std::vector<glm::vec4> coords;

    int c = 0;

    for (p = (const unsigned char*)text; *p; p++)
    {
        float x2 = x + atlas->characters[*p].bitmapLeft * sx;
        float y2 = -y - atlas->characters[*p].bitmapTop * sy;
        float w = atlas->characters[*p].bitmapWidth * sx;
        float h = atlas->characters[*p].bitmapHeight * sy;

        x += atlas->characters[*p].advanceX * sx;
        y += atlas->characters[*p].advanceY * sy;

        if (!w || !h)
            continue;

        coords.push_back(
            glm::vec4(
            x2,
            -y2,
            atlas->characters[*p].uvOffsetX,
            atlas->characters[*p].uvOffsetY)
            );


        coords.push_back(
            glm::vec4(
            x2 + w,
            -y2,
            atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
            atlas->characters[*p].uvOffsetY)
            );



        coords.push_back(
            glm::vec4(
            x2,
            -y2 - h,
            atlas->characters[*p].uvOffsetX,
            atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
            );



        coords.push_back(
            glm::vec4(
            x2 + w,
            -y2,
            atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
            atlas->characters[*p].uvOffsetY)
            );


        coords.push_back(
            glm::vec4(
            x2,
            -y2 - h,
            atlas->characters[*p].uvOffsetX,
            atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
            );


        coords.push_back(
            glm::vec4(
            x2 + w,
            -y2 - h,
            atlas->characters[*p].uvOffsetX + atlas->characters[*p].bitmapWidth / atlas->width,
            atlas->characters[*p].uvOffsetY + atlas->characters[*p].bitmapHeight / atlas->height)
            );
    }

    glBindVertexArray(vao_text);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glActiveTexture(GL_TEXTURE0);
    glUniform1i(atlas->textureUniform, 0);
    glBindTexture(GL_TEXTURE_2D, atlas->texture);

    GLfloat textColor[4] = {0.0, 1.0, 0.0, 0.}; //green
    glUniform4fv(m_uniform_textColor, 1, textColor);

    glBindBuffer(GL_ARRAY_BUFFER, vbo_text);
    glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(glm::vec4), coords.data(), GL_DYNAMIC_DRAW);

    //Position
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)0);

    glDrawArrays(GL_TRIANGLES, 0, coords.size());

    glDisableVertexAttribArray(0);

    glBindVertexArray(0);
    glUseProgram(0);
}

TextVertexShader.vertexshader:

#version 330 core

layout(location = 0) in vec4 pos_uv;
out vec2 uv;

void main(void) {
  gl_Position = vec4(pos_uv.xy, 0, 1);
  uv = pos_uv.zw;
}

TextFragmentShader.fragmentshader:

#version 330 core

in vec2 uv;
uniform sampler2D texture;
uniform vec4 textColor;

out vec4 color;

void main(void) {
  color = vec4(textColor.rgb, texture2D(texture, uv).a);
}

2条回答
叼着烟拽天下
2楼-- · 2019-01-28 10:11

Your texture format is R8, so it contains R channel only. Your shader seams to use A channel. Compare these 2 lines of code:

glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, 
             GL_UNSIGNED_BYTE, 0);
 ...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width,
                glyphSlot->bitmap.rows, GL_RED, ...

vs

color = vec4(textColor.rgb, texture2D(texture, uv).a);

I'd suggest to change GL_R8 & GL_RED to GL_ALPHA8 & GL_ALPHA if your OpenGL < 3.0 or change texture2D(texture, uv).a to texture2D(texture, uv).r otherwise.

查看更多
再贱就再见
3楼-- · 2019-01-28 10:20

OP here. This is my working code, as per Anonymous' suggestion:

In struct FontCharacterAtlas:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, 
             GL_UNSIGNED_BYTE, 0);
 ...
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, 
                GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);

In TextFragmentShader.fragmentshader:

color = vec4(textColor.rgb, texture2D(texture, uv).r);

Text renders, albeit not very nicely (some characters' sides are slightly cut off, and when using a24 or a12 atlas the font is quite messy) but that's another story.

查看更多
登录 后发表回答