Weird result using when using texture atlas with Freetype2

Started by
2 comments, last by Ghost_RacCooN 7 years, 6 months ago

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:

53telx.jpg

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?

Advertisement

Ive had a problem similar to this with gl text before, although it was a while ago and I dont remember what it was exactly. What gl version are you targeting and what does the shader look like for this?

Have you rendered the atlas to a quad and confirmed that everything is copying into the right place? Off the top of my head I remember something about setting the textures swizzle tex parameter GL_TEXTURE_SWIZZLE_RGBA to {GL_RED, GL_RED, GL_RED, GL_RED} when using the format GL_RED but thats just a guess.

1) confirm your tex coords are generated correctly

2) confirm the image displays as you think it should

3) confirm you are getting the correct values in the shader when sampling the texture in the fragment shader

You're rendering as a triangle strip. I don't think you meant to do that.

Thanks for the replies! Apparently yes it was because of GL_TRIANGLE_STRIP instead of GL_TRIANGLES, I think it must've auto corrected and then I didn't even think twice but now it works like it should. Man I feel so stupid but thanks for the help

This topic is closed to new replies.

Advertisement