我目前正在尝试改进我的文字渲染。你将每个角色分开渲染的基本方法,但现在我想通过渲染纹理图集在一个绘图调用中做所有事情。纹理图集几乎完成了。它确实将文本渲染为四边形,但我无法弄清楚如何解决alpha问题。值R,G和B均为零。
我也尝试过GL_ALPHA
,GL_RGBA
和GL_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!\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
提供的缓冲区是具有单个颜色通道的缓冲区。由于使用了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_NEAREST
或GL_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;