Search code examples
opengltexturesfreetype

use of texture arrays with freetype in opengl


After having read and run the following tutorial (http://learnopengl.com/code_viewer.php?code=in-practice/text_rendering), I learned how to render text using freetype in OpenGL. I am now wondering if it is possible to avoid calling glDrawArrays for each glyph. Hence I have done some modifications on the VBO to use it for whole string instead of one glyph. As a first step I have used the string "AA" and since both glyphs are identical, they also share the same texture. So it was not an issue to get the following code running:

glGenVertexArrays(1, & textVAO);
glBindVertexArray(textVAO);
glGenBuffers(1, &textVBO);
glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferData(GL_ARRAY_BUFFER, 24* 2* sizeof(float), NULL, GL_DYNAMIC_DRAW);
glVertexPointer( 4, GL_FLOAT, 0, NULL);

followed by:

textShader.Use();
glm::vec3 color;
color = glm::vec3(1.0, 0.7f, 0.9f);
glUniform3f(glGetUniformLocation(textShader.Program, "textColor"), color.x, color.y, color.z);
glBindVertexArray(textVAO);
glActiveTexture(GL_TEXTURE0);
int k=0;
for(c = text.begin(); c != text.end(); c++){
    Character ch = Characters[*c];
    GLfloat xpos = x + ch.Bearing.x * scale;
    GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
    GLfloat w = ch.Size.x * scale;
    GLfloat h = ch.Size.y * scale;
    V.block(0,k,24,1) << xpos, ypos + h,0.0,0.0,xpos, ypos,0.0,1.0,xpos + w, ypos,1.0,1.0,xpos, ypos + h,0.0,0.0,xpos + w,ypos,1.0,1.0,xpos + w, ypos+h,1.0,0.0;
    k++;
    x += ( (ch.Advance >> 6) * scale);
}
glBindTexture(GL_TEXTURE_2D,66);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 24* 2* sizeof(float), V.data());
glDrawArrays(GL_TRIANGLES,0,4*3);
glBindTexture(GL_TEXTURE_2D,0);
glDisableClientState(GL_VERTEX_ARRAY);
glUseProgramObjectARB(0);

I would like to be able to render "AB" or "ZW" on the screen so I am now trying to use a GL_TEXTURE_2D_ARRAY together with glTexImage3D and glTexSubImage3D. Again for simplification I use "AA" in order to have the same width and height for the glyphs. So I added

GLuint textureArray;
glEnable(GL_TEXTURE_2D_ARRAY);
glGenTextures(1, &textureArray);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, 4, 30, 35, 2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);

to the first part of the precedent code and the second part has become:

textShader.Use();
glm::vec3 color;
color = glm::vec3(1.0, 0.7f, 0.9f);
glUniform3f(glGetUniformLocation(textShader.Program, "textColor"), color.x, color.y, color.z);
glBindVertexArray(textVAO);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D_ARRAY);
for(c = text.begin(); c != text.end(); c++) {
    Character ch = Characters[*c];
    GLfloat xpos = x + ch.Bearing.x * scale;
    GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
    GLfloat w = ch.Size.x * scale;
    GLfloat h = ch.Size.y * scale;
    V.block(0,k,24,1) << xpos, ypos + h,0.0,0.0,xpos, ypos,0.0,1.0,xpos + w, ypos,1.0,1.0,xpos, ypos + h,0.0,0.0,xpos + w,ypos,1.0,1.0,xpos + w, ypos+h,1.0,0.0;
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, k, 30,35, 1, GL_RED, GL_UNSIGNED_BYTE, ch.pointeur);
    k++;
    x += ( (ch.Advance >> 6) * scale);
    }
glBindTexture(GL_TEXTURE_2D_ARRAY,textureArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 24* 2* sizeof(float), V.data());
glDrawArrays(GL_TRIANGLES,0,4*3);
glBindTexture(GL_TEXTURE_2D,0);
glDisableClientState(GL_VERTEX_ARRAY);
glUseProgramObjectARB(0);

The code compiles, but I don't get anything on the screen. So I wonder if I am using correctly the GL_TEXTURE_2D_ARRAY and also if I have to do an "array version" of the shaders. I am using OpenGL 4.5, Thank you.


Solution

  • There are (at least) two problems.

    First, you are using the fixed-function pipeline, which cannot be used with array textures. The texture environment accessing stuff does not know how to handle them. If you want to use array textures, you must use shaders.

    Second, even if you were using shaders, you are doing this all wrong. Then again, you were taught wrongly by the tutorial (which in this case is teaching such bad practice that it's actively damaging to OpenGL users). You put each glyph in its own array layer. Well, many OpenGL implementations only support 256 array layers, which is not a lot of glyphs if you want to include non-English text.

    The correct way to do glyph rendering is to build a texture atlas of glyphs, rather than using a glyph-per-texture (as the crappy tutorial does) or a glyph-per-array-layer (as you do). You put multiple glyphs in different locations of a single 2D texture, then use texture coordinates to pick which glyph to use. That will allow you to submit entire blocks of text with a single draw call.

    2D texture sizes can be upwards of 16K in pixels these days. Even with only 4096x4096 textures, you can fit over 16 thousand 32x32 glyphs into them.