FreeType - 纹理图集 - 为什么我的文字渲染为四边形?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeType - 纹理图集 - 为什么我的文字渲染为四边形?相关的知识,希望对你有一定的参考价值。

我目前正在尝试改进我的文字渲染。你将每个角色分开渲染的基本方法,但现在我想通过渲染纹理图集在一个绘图调用中做所有事情。纹理图集几乎完成了。它确实将文本渲染为四边形,但我无法弄清楚如何解决alpha问题。值R,G和B均为零。

我也尝试过GL_ALPHAGL_RGBAGL_LUMINANCE

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

所有字体字符的图像:

着色器:

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!
", 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!
", 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
", 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提供的缓冲区是具有单个颜色通道的缓冲区。由于使用了OpenGL ES,纹理的源格式必须是GL_LUMINANCE

使用单个(红色)颜色通道指定二维纹理图像。纹理行的对齐必须设置为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 mapmapping,因此纹理缩小函数必须是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,因此必须从红色,绿色或蓝色通道读取样本:

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

以上是关于FreeType - 纹理图集 - 为什么我的文字渲染为四边形?的主要内容,如果未能解决你的问题,请参考以下文章

SpriteKit animateWithTextures 不适用于纹理图集

iOS使用OpenGLES和freetype绘制字体

“纹理图集不支持模板渲染”SpriteKit 和 Xcode

将纹理图集拆分为多个纹理图集警告

SpriteKit 在哪里为数千个精灵加载纹理图集

如何使我的 spriteNode 纹理通用?