Sign in to follow this  
Finalspace

OpenGL How to rendering fast bitmap fonts/text

Recommended Posts

Finalspace    1140

Hi there,

i have implemented signed distance field bitmap fonts creation and rendering successfully in a simple opengl sample.
The rendering is a mix of modern shader stuff and immediate mode for translating and scaling the characters, but this are not allowed in my actual game engine. Only opengl es 2.0 compatible rendering is allowed, no pushmatrix, no translate, scale etc. What is the fastest way possible to convert the following code into a 100% opengl es 2.0 compatible way - just the todo part?

 

I dont think its a good way to do for every character two matrix operations (scale and translate) and one operation to reset the matrix before the drawing. Is there a way to improve that - can these be moved to the shader somehow ?
 

	
	private void drawText(String text, float x, float y, float scale, HorizontalAlignment halign, VerticalAlignment valign) {
		// Enable alpha blending
		GL11.glEnable(GL11.GL_BLEND);

		// Enable vertex buffer - will also enabled client state for texture and vertex array
		fontVBO.enable();

		// Set white color
		GL11.glColor4f(1f, 1f, 1f, 1f);

		// Calculate text sizes in world coordinates
		float textSizeX = getTextWidth(text, scale);
		float textSizeY = getTextHeight(text, scale);

		// Calculate starting text x and y offset in world coordinates
		float sxoffset = -textSizeX * 0.5f;
		float syoffset = 0f;
		if (HorizontalAlignment.Left.equals(halign)) {
			sxoffset += textSizeX * 0.5f;
		} else if (HorizontalAlignment.Right.equals(halign)) {
			sxoffset -= textSizeX * 0.5f;
		}
		if (VerticalAlignment.Bottom.equals(valign)) {
			syoffset = textSizeY * 0.5f;
		} else if (VerticalAlignment.Top.equals(valign)) {
			syoffset = -textSizeY * 0.5f;
		}

		// Enable bitmap font texture
		fontTex.enable(0);
		
		// Enable sdf shader when enabled
		if (sdfEnabled) {
			sdfShader.enable();
			sdfShader.setUniform1i(sdfShaderTex0Location, 0);
		}
		
		// TODO: Rendering the characters - each char is moved and scaled in immediate mode which does not work with opengl es 2.0!!!
		float xoffset = sxoffset;
		float yoffset = syoffset;
		for (int i = 0; i < text.length(); i++) {
			BitmapFontChar fontChar = bitmapFonts.get(text.codePointAt(i));
			GL11.glPushMatrix();
			GL11.glTranslatef(x + xoffset + fontChar.getAdvance() * scale * 0.5f, y + yoffset, 0f);
			GL11.glScalef(scale, scale, 1f);
			GL11.glDrawArrays(GL11.GL_QUADS, fontChar.getVboOffset(), 4);
			GL11.glPopMatrix();
			xoffset += fontChar.getAdvance() * scale;
		}
		
		// Disable sdf shader when enabled
		if (sdfEnabled) {
			sdfShader.disable();
		}

		// Disable bitmap font texture
		fontTex.disable(0);

		/*
		GL11.glPolygonMode( GL11.GL_FRONT, GL11.GL_LINE );
		xoffset = sxoffset;
		yoffset = syoffset;
		for (int i = 0; i < text.length(); i++) {
			BitmapFontChar fontChar = bitmapFonts.get(text.codePointAt(i));
			GL11.glPushMatrix();
			GL11.glTranslatef(x + xoffset + fontChar.getAdvance() * 0.5f, y + yoffset, 0f);
			GL11.glDrawArrays(GL11.GL_QUADS, fontChar.getVboOffset(), 4);
			GL11.glPopMatrix();
			xoffset += fontChar.getAdvance();
			//yoffset -= 0.01f;
		}
		GL11.glPolygonMode( GL11.GL_FRONT, GL11.GL_FILL );
		*/

		// Disable vertex buffer - will also disable client state for texture and vertex array
		fontVBO.disable();

		// Disable alpha blending
		GL11.glDisable(GL11.GL_BLEND);
	}
Edited by Finalspace

Share this post


Link to post
Share on other sites
alh420    5995

Create the matrix with some other matrix library, that is opengl compatible.

 

Then add something like this to your shader:

 

  gl_Position = u_matrix * a_position;

 

Then you should be able to set your matrix with setUniform and everything should work as before.

Share this post


Link to post
Share on other sites
Finalspace    1140

I have already done this, but is this really the fastest way to do this?

				// Render chars
				float xoffset = sxoffset;
				float yoffset = syoffset;
				for (int i = 0; i < text.length(); i++) {
					TextureFontChar tfc = texFont.getChar(text.codePointAt(i));
					if (tfc == null) {
						// Char is not found, use first one which should the space character
						tfc = texFont.getChar(0);
					}
					currentMatrix.set(orthoMatrix);
					currentMatrix.translate(x + xoffset + tfc.getAdvance() * 0.5f * scale, y + yoffset, 0f);
					currentMatrix.scale(scale, scale, 1f);
					renderer.setTransform(currentMatrix);
					renderer.drawVertexArray(ElementType.Quad, tfc.getVboOffset(), 4);
					xoffset += tfc.getAdvance() * 2f * scale;
				}

Edited by Finalspace

Share this post


Link to post
Share on other sites
Zot    140
Can't you just set up your quads first and then call just one DrawArrays for the whole screen full of text?
You can make it that whenever you call your text drawing code, it does NOT draw it but builds up the quad array. The text can then be all drawn with one call before you swap buffers, or whenever you like of course.

Share this post


Link to post
Share on other sites
alh420    5995

Ah, sorry, I thought you meant "least change" with fastest.

 

If you are not animating the individual characters every frame, it would be faster to generate the vertexes into a vertex-array, and then re-use that until the text has to be changed.

Edited by Olof Hedman

Share this post


Link to post
Share on other sites
Finalspace    1140

Ah, sorry, I thought you meant "least change" with fastest.

 

If you are not animating the individual characters every frame, it would be faster to generate the vertexes into a vertex-array, and then re-use that until the text has to be changed.

 

This may work to modify the vertex buffer and fill it with all the letter quads - should be faster than using 2 matrix operations per letter. Why i havent thought about that.... thanks for that hint! The only thing which sucks - filling the vertex buffer with java is a pain in the pass :-(

 

This is so .... inconvenient (This is done once to create the letters from the bitmap font in the vertex buffer initialy):

// Creating a vertex buffer for all letters/quads
fontVBO = renderer.createVertexBuffer(false, vertexCount, VertexBufferFormat.TEX2_VERTEX4_PAD2);

// Creating a damn byte buffer which is required for the fill vertex buffer method
ByteBuffer vertBuffer = ByteBuffer.allocateDirect(VertexBufferFormat.TEX2_VERTEX4_PAD2.getStride() * vertexCount).order(ByteOrder.nativeOrder());
		
// We want to fill floats into the byte buffer - so we need to convert it back to a float buffer -.-
FloatBuffer fontVertices = vertBuffer.asFloatBuffer();

// Put stuff in it
...
fontVertices.put(new float[] { u, v, x, y, z, w, 0f, 0f });
...

// Finally flip the buffer to write the bytes left to the final byte buffer
// This will also reset the postition to zero
fontVertices.flip();

// And lastly fill the vertex buffer with that byte buffer content
fontVBO.fill(vertBuffer);

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By ZeldaFan555
      Hello, My name is Matt. I am a programmer. I mostly use Java, but can use C++ and various other languages. I'm looking for someone to partner up with for random projects, preferably using OpenGL, though I'd be open to just about anything. If you're interested you can contact me on Skype or on here, thank you!
      Skype: Mangodoor408
    • By tyhender
      Hello, my name is Mark. I'm hobby programmer. 
      So recently,I thought that it's good idea to find people to create a full 3D engine. I'm looking for people experienced in scripting 3D shaders and implementing physics into engine(game)(we are going to use the React physics engine). 
      And,ye,no money =D I'm just looking for hobbyists that will be proud of their work. If engine(or game) will have financial succes,well,then maybe =D
      Sorry for late replies.
      I mostly give more information when people PM me,but this post is REALLY short,even for me =D
      So here's few more points:
      Engine will use openGL and SDL for graphics. It will use React3D physics library for physics simulation. Engine(most probably,atleast for the first part) won't have graphical fron-end,it will be a framework . I think final engine should be enough to set up an FPS in a couple of minutes. A bit about my self:
      I've been programming for 7 years total. I learned very slowly it as "secondary interesting thing" for like 3 years, but then began to script more seriously.  My primary language is C++,which we are going to use for the engine. Yes,I did 3D graphics with physics simulation before. No, my portfolio isn't very impressive. I'm working on that No,I wasn't employed officially. If anybody need to know more PM me. 
       
    • By Zaphyk
      I am developing my engine using the OpenGL 3.3 compatibility profile. It runs as expected on my NVIDIA card and on my Intel Card however when I tried it on an AMD setup it ran 3 times worse than on the other setups. Could this be a AMD driver thing or is this probably a problem with my OGL code? Could a different code standard create such bad performance?
    • By Kjell Andersson
      I'm trying to get some legacy OpenGL code to run with a shader pipeline,
      The legacy code uses glVertexPointer(), glColorPointer(), glNormalPointer() and glTexCoordPointer() to supply the vertex information.
      I know that it should be using setVertexAttribPointer() etc to clearly define the layout but that is not an option right now since the legacy code can't be modified to that extent.
      I've got a version 330 vertex shader to somewhat work:
      #version 330 uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ModelViewMatrix; layout(location = 0) in vec4 Vertex; layout(location = 2) in vec4 Normal; // Velocity layout(location = 3) in vec3 TexCoord; // TODO: is this the right layout location? out VertexData { vec4 color; vec3 velocity; float size; } VertexOut; void main(void) { vec4 p0 = Vertex; vec4 p1 = Vertex + vec4(Normal.x, Normal.y, Normal.z, 0.0f); vec3 velocity = (osg_ModelViewProjectionMatrix * p1 - osg_ModelViewProjectionMatrix * p0).xyz; VertexOut.velocity = velocity; VertexOut.size = TexCoord.y; gl_Position = osg_ModelViewMatrix * Vertex; } What works is the Vertex and Normal information that the legacy C++ OpenGL code seem to provide in layout location 0 and 2. This is fine.
      What I'm not getting to work is the TexCoord information that is supplied by a glTexCoordPointer() call in C++.
      Question:
      What layout location is the old standard pipeline using for glTexCoordPointer()? Or is this undefined?
       
      Side note: I'm trying to get an OpenSceneGraph 3.4.0 particle system to use custom vertex, geometry and fragment shaders for rendering the particles.
    • By markshaw001
      Hi i am new to this forum  i wanted to ask for help from all of you i want to generate real time terrain using a 32 bit heightmap i am good at c++ and have started learning Opengl as i am very interested in making landscapes in opengl i have looked around the internet for help about this topic but i am not getting the hang of the concepts and what they are doing can some here suggests me some good resources for making terrain engine please for example like tutorials,books etc so that i can understand the whole concept of terrain generation.
       
  • Popular Now