c++ - FreeType - Texture Atlas - 为什么我的文本呈现为四边形?

标签 c++ opengl-es glsl freetype texture-atlas

我目前正在尝试改进我的文本呈现。单独渲染每个角色的基本方法有效,但现在我想通过渲染纹理图集在一次绘制调用中完成所有操作。 纹理图集差不多完成了。它确实将文本呈现为四边形,但我不知道如何解决 alpha 问题。 RGB 的值是全部为零。

我还尝试了 GL_ALPHAGL_RGBAGL_LUMINANCE .

注意:我正在使用 Raspberry Pi 并使用 OpenGL ES 2.0。

所有字体字符的图片: An image of the text

着色器:

precision mediump float;

attribute vec4 vertex;

varying vec2 textCoord;

void main()
{
   gl_Position = vec4(vertex.xy, 0.0, 1.0);
   textCoord = vertex.zw;
}
precision lowp float; 

varying vec2 textCoord;

uniform sampler2D text;
uniform vec3 textColor;

void main()
{
    lowp vec4 sampled = vec4(1.0, 1.0, texture2D(text, textCoord).ba);
    gl_FragColor = vec4(textColor, 1.0) * sampled;
}

设置纹理图集:

glGenBuffers(1, &vbo);

if (FT_Init_FreeType(&m_FT)) {
    std::cout << "ERROR: Could not init the FreeType Library" << std::endl;
}

if (FT_New_Face(m_FT, "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 0, &m_Face)) {
    std::cout << "ERROR: This font failed to load." << std::endl;
}

FT_Set_Pixel_Sizes(m_Face, 0, height);

unsigned int roww = 0;
unsigned int rowh = 0;

memset(c, 0, sizeof c); // Set all values to 0

// Find minimum size for a texture holding all visible ASCII characters 
for (int i = 0; i < 128; i++) {
    if (FT_Load_Char(m_Face, i, FT_LOAD_RENDER)) {
        fprintf(stderr, "Loading character %c failed!\n", i);
        continue;
    }
    if (roww + m_Face->glyph->bitmap.width + 1 >= SCREEN_WIDTH) {
        w = std::max(w, roww);
        h += rowh;
        roww = 0;
        rowh = 0;
    }
    roww += m_Face->glyph->bitmap.width + 1;
    rowh = std::max(rowh, m_Face->glyph->bitmap.rows);
}

w = std::max(w, roww);
h += rowh;

// Create a texture that will be used to hold all ASCII glyphs 
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);

GetShader()->Use();
GetShader()->SetInt("text", tex);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

// We require 1 byte alignment when uploading texture data 
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// Paste all glyph bitmaps into the texture, remembering the offset 
int ox = 0;
int oy = 0;

rowh = 0;

for (int i = 0; i < 128; i++) {
    if (FT_Load_Char(m_Face, i, FT_LOAD_RENDER)) {
        fprintf(stderr, "Loading character %c failed!\n", i);
        continue;
    }

    if (ox + m_Face->glyph->bitmap.width + 1 >= SCREEN_WIDTH) {
        oy += rowh;
        rowh = 0;
        ox = 0;
    }


    glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, m_Face->glyph->bitmap.width, m_Face->glyph->bitmap.rows, GL_RGBA, GL_UNSIGNED_BYTE, m_Face->glyph->bitmap.buffer);
    c[i].ax = m_Face->glyph->advance.x >> 6;
    c[i].ay = m_Face->glyph->advance.y >> 6;

    c[i].bw = m_Face->glyph->bitmap.width;
    c[i].bh = m_Face->glyph->bitmap.rows;

    c[i].bl = m_Face->glyph->bitmap_left;
    c[i].bt = m_Face->glyph->bitmap_top;

    c[i].tx = ox / (float)w;
    c[i].ty = oy / (float)h;

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

fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024);

GetShader()->Stop();

glBindTexture(GL_TEXTURE_2D, 0);

绘图函数:

// Set uniforms
    a_Shader->SetVec3("textColor", m_v3Color);

    glActiveTexture(GL_TEXTURE0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

    point coords[6 * 128];
    int dc = 0;

    const uint8_t *p;

    float sx = 2.0 / SCREEN_WIDTH;
    float sy = 2.0 / SCREEN_HEIGHT;

//  float x = (m_v2Position.x - (SCREEN_WIDTH / 2)) * sx;
    //float y = (m_v2Position.y - (SCREEN_HEIGHT / 2)) * sy;

    float x = -1.f;
    float y = 0.f;

    // Loop through all characters 
    for (int p = 0; p < 128; p++) {
        // Calculate the vertex and texture coordinates 
        float x2 = x + c[p].bl * sx;
        float y2 = -y - c[p].bt * sy;
        float w = c[p].bw * sx;
        float h = c[p].bh * sy;

        // Advance the cursor to the start of the next character 
        x += c[p].ax * sx;
        y += c[p].ay * sy;

        // Skip glyphs that have no pixels 
        if (!w || !h)
            continue;

        coords[dc++] = (point) {
            x2, -y2, c[p].tx, c[p].ty
        };
        coords[dc++] = (point) {
            x2 + w, -y2, c[p].tx + c[p].bw / w, c[p].ty
        };
        coords[dc++] = (point) {
            x2, -y2 - h, c[p].tx, c[p].ty + c[p].bh / h
        };
        coords[dc++] = (point) {
            x2 + w, -y2, c[p].tx + c[p].bw / w, c[p].ty
        };
        coords[dc++] = (point) {
            x2, -y2 - h, c[p].tx, c[p].ty + c[p].bh / h
        };
        coords[dc++] = (point) {
            x2 + w, -y2 - h, c[p].tx + c[p].bw / w, c[p].ty + c[p].bh / h
        };
    }

    // Render glyph texture over quad
    glBindTexture(GL_TEXTURE_2D, tex);

    // Update content of VBO memory
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_DYNAMIC_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Render quad
    glDrawArrays(GL_TRIANGLES, 0, dc);

    glDisableVertexAttribArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glCheckError(); 

最佳答案

m_Face->glyph->bitmap.buffer 提供的缓冲区是一个单一颜色 channel 的缓冲区。由于使用了 OpenGL ES,纹理的源格式必须是 GL_LUMINANCE

指定具有单一(红色)颜色 channel 的二维纹理图像。一行纹理的对齐方式必须设置为 1(参见 glPixelStorei)。注意默认值为 4,这与每像素 1 字节大小的紧密压缩纹理不匹配:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

请注意,由于纹理不是 2 的幂,因此不能使用 mip 映射,因此纹理缩小函数必须是 GL_NEARESTGL_LINEAR 并且环绕模式必须是 GL_CLAMP_TO_EDGE:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

将字形加载到纹理:

glTexSubImage2D(
    GL_TEXTURE_2D, 0, ox, oy, 
    m_Face->glyph->bitmap.width, m_Face->glyph->bitmap.rows,
    GL_LUMINANCE, GL_UNSIGNED_BYTE, m_Face->glyph->bitmap.buffer);

由于内部纹理格式是 GL_LUMINANCE,因此必须从红色、绿色或蓝色 channel 读取样本:

lowp float sampled = texture2D(text, textCoord).r;
gl_FragColor = vec4(textColor, 1.0) * sampled;

关于c++ - FreeType - Texture Atlas - 为什么我的文本呈现为四边形?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55892503/

相关文章:

java - 对 glGetString() 的静态调用在 Android 5.0 中返回 null

python - 片段着色器 : get distance from fragment to bezier curve

opengl - glsl 中的 "flat"限定符?

c++ - 从 lambda 函数构造的 boost::function_output_iterator 不可赋值

c++ - 'sqrt' 不是 'std' 的成员

opengl-es - 如何绕过 GL_MAX_TEXTURE_SIZE 进行图像处理

java - Android:OpenGL-ES 游戏图像 (HUD)

python - 如何给三角形添加纹理?

c++ - 如何删除一次制作的所有纹理?

c++ - std::stringstream::flush() 应该做任何事情吗?