I've been trying to get font rendering to work, and I got the atlas to work, but when I render the text I end up with this:
Basically whenever I render more than one character from the Atlas it becomes messed up, I've checked if the offset is wrong and it seems to be fine and the texture coordinates are aligned with the vertex positions. I'm really not sure what might be wrong, here's the complete code:
#define MAX_WIDTH 1024
namespace FontFactory
{
struct FreeTypeFontCharacter
{
GLfloat mAdvanceX; // advance: the horizontal advance e.g. the horizontal distance (in 1/64th pixels) from the origin to the origin of the next glyph. Accessed via face->glyph->advance.x.
GLfloat mAdvanceY; // same as above but for Y
GLfloat mBitmapWidth; // width: the width (in pixels) of the bitmap accessed via face->glyph->bitmap.width.
GLfloat mBitmapHeight; // height: the height (in pixels) of the bitmap accessed via face->glyph->bitmap.rows.
GLfloat mBitmapLeft; // bearingX: the horizontal bearing e.g. the horizontal position (in pixels) of the bitmap relative to the origin accessed via face->glyph->bitmap_left.
GLfloat mBitmapTop; // bearingY: the vertical bearing e.g. the vertical position (in pixels) of the bitmap relative to the baseline accessed via face->glyph->bitmap_top.
GLfloat mUVOffsetX;
GLfloat mUVOffsetY;
};
struct FreeTypeTextureAtlas
{
FreeTypeTextureAtlas(FT_Face& face, int height, GLuint tUniform)
{
//Font = Face
//Glyph = Character
FT_Set_Pixel_Sizes(face, 0, height);
FT_GlyphSlot glyphSlot = face->glyph;
float rowWidth = 0;
float rowHeight = 0;
mWidth = 0;
mHeight = 0;
memset(mCharacters, 0, sizeof(mCharacters));
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 (rowWidth + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
{
mWidth = std::fmax(mWidth, rowWidth);
mHeight += rowHeight;
rowWidth = 0;
rowHeight = 0;
}
rowWidth += glyphSlot->bitmap.width + 1;
rowHeight = std::fmax(rowHeight, glyphSlot->bitmap.rows);
}
mWidth = std::fmax(mWidth, rowWidth);
mHeight += rowHeight;
glGenTextures(1, &mTextureID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glUniform1i(tUniform, 0);
mTextureUniform = tUniform;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, mWidth, mHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//glPixelStorei(GL_PACK_ALIGNMENT, 1);
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);
float ox = 0;
float oy = 0;
for (int i = 32; i < 128; i++)
{
if (FT_Load_Char(face, i, FT_LOAD_RENDER))
continue;
if (ox + glyphSlot->bitmap.width + 1 >= MAX_WIDTH)
{
oy += rowHeight;
rowHeight = 0;
ox = 0;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
mCharacters[i].mAdvanceX = glyphSlot->advance.x >> 6;
mCharacters[i].mAdvanceY = glyphSlot->advance.y >> 6;
mCharacters[i].mBitmapWidth = glyphSlot->bitmap.width;
mCharacters[i].mBitmapHeight = glyphSlot->bitmap.rows;
mCharacters[i].mBitmapLeft = glyphSlot->bitmap_left;
mCharacters[i].mBitmapTop = glyphSlot->bitmap_top;
mCharacters[i].mUVOffsetX = ox / (GLfloat)mWidth;
mCharacters[i].mUVOffsetY = oy / (GLfloat)mHeight;
rowHeight = std::fmax(rowHeight, glyphSlot->bitmap.rows);
ox += glyphSlot->bitmap.width + 1;
}
}
~FreeTypeTextureAtlas()
{
glDeleteTextures(1, &mTextureID);
}
GLfloat mWidth;
GLfloat mHeight;
GLuint mTextureID;
GLuint mTextureUniform;
FreeTypeFontCharacter mCharacters[128];
GLuint mDepth;
};
class TextRenderer
{
public:
FT_Library library;
FT_Face face;
FreeTypeTextureAtlas* a48;
FreeTypeTextureAtlas* a24;
FreeTypeTextureAtlas* a12;
GLuint vbo;
GLuint vao;
GLfloat m_posUV;
GLuint m_colorIN;
GLuint m_texture;
int Initialize(Shader& shader)
{
if (FT_Init_FreeType(&library))
{
std::cout << "Could not Initialize freetype library.\n";
return 0;
}
if (FT_New_Face(library, "arialbd.ttf", 0, &face))
{
std::cout << "Could not open font arialbd.ttf\n";
return 0;
}
m_colorIN = shader.getUniformLocation("inputColor");
m_texture = shader.getUniformLocation("texture");
glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
a48 = new FreeTypeTextureAtlas(face, 48, m_texture);
a24 = new FreeTypeTextureAtlas(face, 24, m_texture);
a12 = new FreeTypeTextureAtlas(face, 12, m_texture);
}
void Render(Shader& shader, GLfloat inwidth, GLfloat inheight)
{
float sx = 2.0 / inwidth;
float sy = 2.0 / inheight;
RenderText("JJJ", a48,
-1 + 9.5 * sx, 1 - 190.5 * sy, sx, sy, shader);
RenderText("o", a48,
-1 + 18.5 * sx, 1 - 230.5 * sy, sx, sy, shader);
RenderText("x x x", a48,
-1 + 26.5 * sx, 1 - 270.5 * sy, sx, sy, shader);
}
void RenderText(const char* text, FreeTypeTextureAtlas* atlas, float x, float y, float sx, float sy, Shader& shader)
{
shader.bindShader();
const unsigned char* p;
std::vector<glm::vec4> coords;
float lastx2 = 0;
float lasty2 = 0;
float lastw = 0;
float lasth = 0;
int counter = 0;
for (p = (const unsigned char*)text; *p; p++)
{
unsigned char test = *p;
float x2 = x + atlas->mCharacters[*p].mBitmapLeft * sx;
float y2 = -y - atlas->mCharacters[*p].mBitmapTop * sy;
float w = atlas->mCharacters[*p].mBitmapWidth * sx;
float h = atlas->mCharacters[*p].mBitmapHeight * sy;
x += atlas->mCharacters[*p].mAdvanceX * sx;
y += atlas->mCharacters[*p].mAdvanceY * sy;
if ((lastx2 == x2) || (lasty2 == y2) || (lastw == w) || (lasth == h))
counter++;
lastx2 = x2;
lasty2 = y2;
lastw = w;
lasth = h;
if (!w || !h)
continue;
coords.push_back( //1
glm::vec4(
x2,
-y2,
atlas->mCharacters[*p].mUVOffsetX,
atlas->mCharacters[*p].mUVOffsetY)
);
coords.push_back( // 2
glm::vec4(
x2 + w,
-y2,
atlas->mCharacters[*p].mUVOffsetX + (atlas->mCharacters[*p].mBitmapWidth / atlas->mWidth),
atlas->mCharacters[*p].mUVOffsetY)
);
coords.push_back( // 3
glm::vec4(
x2,
-y2 - h,
atlas->mCharacters[*p].mUVOffsetX,
atlas->mCharacters[*p].mUVOffsetY + (atlas->mCharacters[*p].mBitmapHeight / atlas->mHeight))
);
coords.push_back( // 4
glm::vec4(
x2 + w,
-y2,
atlas->mCharacters[*p].mUVOffsetX + (atlas->mCharacters[*p].mBitmapWidth / atlas->mWidth),
atlas->mCharacters[*p].mUVOffsetY)
);
coords.push_back( // 5
glm::vec4(
x2,
-y2 - h,
atlas->mCharacters[*p].mUVOffsetX,
atlas->mCharacters[*p].mUVOffsetY + (atlas->mCharacters[*p].mBitmapHeight / atlas->mHeight))
);
coords.push_back( // 6
glm::vec4(
x2 + w,
-y2 - h,
atlas->mCharacters[*p].mUVOffsetX + (atlas->mCharacters[*p].mBitmapWidth / atlas->mWidth),
atlas->mCharacters[*p].mUVOffsetY + (atlas->mCharacters[*p].mBitmapHeight / atlas->mHeight))
);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glActiveTexture(GL_TEXTURE0);
glUniform1i(atlas->mTextureUniform, 0);
glBindTexture(GL_TEXTURE_2D, atlas->mTextureID);
shader.setUniform4f("inputColor", glm::vec4(0, 1, 0, 1));
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(glm::vec4), coords.data(), GL_DYNAMIC_DRAW);
glBindVertexArray(vao);
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);
shader.unbindShader();
}
};
}
It's based off this link https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02 but I can't quite get it right, has anyone had this result before?