OpenGL-Schriftwiedergabe mit Freetype2
Ich versuche, eine Freetype-Schriftart mit OpenGL zu rendern, wie im Beispiel unter beschriebenhttp://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02.
Ich konnte einen Texturatlas aus der Schriftart erstellen, Shader erstellen und Quads erstellen. Anscheinend stecke ich fest, indem ich die Textur an den Shader weitergebe und / oder die richtigen UVs für meine Quads erhalte. Kämpfe jetzt schon eine Weile und brauche wirklich die Hilfe.
Das Folgende ist die Struktur, mit der ich meinen Texturatlas erstelle.
struct FontCharacter
{
float advanceX;
float advanceY;
float bitmapWidth;
float bitmapHeight;
float bitmapLeft;
float bitmapTop;
float uvOffsetX;
float uvOffsetY;
};
struct FontTextureAtlas
{
GLuint texture;
GLuint textureUniform;
int width;
int height;
FontCharacter characters[128];
FontTextureAtlas(FT_Face face, int h, GLuint 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))
{
std::cout << "Loading character %c failed\n", i;
continue;
}
if (roww + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
{
width = std::fmax(width, roww);
height += rowh;
roww = 0;
rowh = 0;
}
roww += glyphSlot->bitmap.width + 1;
rowh = std::fmax(rowh, glyphSlot->bitmap.rows);
}
width = std::fmax(width, roww);
height += rowh;
glGenTextures(1, &texture);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glGenTextures failed\n";
}
glActiveTexture(GL_TEXTURE0 + texture);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glActiveTexture failed\n";
}
glBindTexture(GL_TEXTURE_2D, texture);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glBindTexture failed\n";
}
glUniform1i(tUniform, 0);
textureUniform = tUniform;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glTexImage2D failed\n";
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glPixelStorei failed\n";
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glTexParameteri failed\n";
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glTexParameteri failed\n";
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "glTexParameteri failed\n";
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (glGetError() != GL_NO_ERROR)
{
std::cout << "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))
{
std::cout << "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)
{
std::cout << "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::fmax(rowh, glyphSlot->bitmap.rows);
ox += glyphSlot->bitmap.width + 1;
}
std::cout << "Generated a " << width << "x " << height << " (" << width * height / 1024 << " kb) texture atlas.\n";
}
~FontTextureAtlas()
{
glDeleteTextures(1, &texture);
}
Lokale Variablen und Funktionsköpfe, die im Renderer verwendet werden
class RenderCore
{
FT_Library library;
FT_Face face;
FontTextureAtlas* a48;
FontTextureAtlas* a24;
FontTextureAtlas* a12;
GLuint vbo;
GLuint vao;
GLuint m_posUV;
GLuint m_colorIN;
GLuint m_texture;
int InitFT();
void RenderText(const char* text, FontTextureAtlas* atlas, float x, float y, float sx, float sy);
}
Hier lade ich meine Schriften.
int RenderCore::InitFT()
{
if (FT_Init_FreeType(&library))
{
std::cout << "Could not Initialize freetype library.\n";
return 0;
}
/* Load a font */
if (FT_New_Face(library, "assets/Fonts/arialbd.ttf", 0, &face))
{
std::cout << "Could not open font assets/Fonts/DentonBeta2.ttf\n";
return 0;
}
m_shaderManager->CreateProgram("Text");
m_shaderManager->LoadShader("shaders/Text.vertex", "TextVS", GL_VERTEX_SHADER);
m_shaderManager->LoadShader("shaders/Text.fragment", "TextFS", GL_FRAGMENT_SHADER);
m_shaderManager->AttachShader("TextVS", "Text");
m_shaderManager->AttachShader("TextFS", "Text");
m_shaderManager->LinkProgram("Text");
m_shaderManager->UseProgram("Text");
m_shaderManager->UseProgram("Text");
m_colorIN = m_shaderManager->GetUniformLocation("Text", "inputColor");
m_texture = m_shaderManager->GetUniformLocation("Text", "texture");
// Create the vertex buffer object
glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
/* Create texture atlasses for several font sizes */
a48 = new FontTextureAtlas(face, 48, m_texture);
a24 = new FontTextureAtlas(face, 24, m_texture);
a12 = new FontTextureAtlas(face, 12, m_texture);
}
Rendering-Funktion.
void RenderCore::RenderText(const char* text, FontTextureAtlas* atlas, float x, float y, float sx, float sy)
{
m_shaderManager->UseProgram("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)
);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0 + atlas->texture);
glUniform1i(atlas->textureUniform, 0);
glBindTexture(GL_TEXTURE_2D, atlas->texture);
m_shaderManager->SetUniform(1, glm::vec4(0, 0, 1, 1), m_colorIN);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(glm::vec4), coords.data(), GL_DYNAMIC_DRAW);
//Generate VAO
glBindVertexArray(vao);
//Position
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)0);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, coords.size());
glDisableVertexAttribArray(0);
m_shaderManager->ResetProgram();
}
Vertex-Shader
#version 440
in vec4 pos_uv;
out vec2 uv;
void main()
{
gl_Position = vec4(pos_uv.xy, 0, 1);
uv = pos_uv.zw;
}
Fragment-Shader
#version 440
in vec2 uv;
uniform sampler2D texture;
uniform vec4 inputColor;
out vec4 color;
void main()
{
color = vec4(inputColor.rgb, texture2D(texture, uv).a);
}
Mit gDebugger kann ich sehen, dass der Texturatlas ordnungsgemäß generiert wurde und der VBO ebenfalls in Ordnung zu sein scheint. Das Ergebnis ist jedoch nur ein paar Quadrate auf dem Bildschirm und ich habe wirklich keine Ahnung warum. Ich denke, es könnte ein Problem mit der Übergabe der Textur an den Shader sein, alle Kanäle mit Ausnahme des Alphakanals sind leer und das Alpha ist immer 1.