1
\$\begingroup\$

What I tried:

I was testing some things in order to render text with stb_truetype.h and OpenGL in C.

I took as a reference the example that appears here. Basically, this example, loads a .ttf file and returns the raw information in bytes, that can be used to generate a texture in OpenGL.

I adapted the example, mentioned before, into modern OpenGL, because, the example uses OpenGL deprecated functions, like glVertex2f. The only thing I get to output on screen was this kind of noise of strange colors:

Noisy output

The code I use:

texture_t fnt_texture;
GLuint fnt_shader;

unsigned char ttf_buffer[1 << 20];
unsigned char temp_bitmap[512 * 512];

stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs

#define FONT_VS\
    "#version 330 core\n"\
    "layout(location = 0) in vec3 m_Position;"\
    "layout(location = 1) in vec2 m_TexCoords;"\
    "out vec2 TexCoords;\n"\
    "void main() {\n"\
    "TexCoords = m_TexCoords;\n"\
    "gl_Position = vec4(m_Position, 1.0);\n"\
    "}\n"\

#define FONT_FS\
    "#version 330 core\n"\
    "in vec2 TexCoords;\n"\
    "uniform sampler2D Texture;\n"\
    "void main() {\n"\
    "gl_FragColor = texture(Texture, TexCoords);\n"\
    "}\n"\

void font_init(void)
{
    fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
    stbtt_BakeFontBitmap(ttf_buffer, 0, 32.0, temp_bitmap, 512, 512, 32, 96, cdata); // no guarantee this fits!

    glGenTextures(1, &fnt_texture[3]); // My texture type, is an array that saves the texture on the 3rd position.
    glBindTexture(GL_TEXTURE_2D, fnt_texture[3]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_bitmap);
    glGenerateMipmap(GL_TEXTURE_2D);

    // can free temp_bitmap at this point
    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);

    glBindTexture(GL_TEXTURE_2D, 0);

    fnt_shader = shader_init(FONT_VS, FONT_FS);
}

void font_render(model_t model) 
{
    shader_bind(fnt_shader);
    texture_bind(fnt_texture, 0);

    model_begin(model);
    model_draw(model, GL_TRIANGLES); // The model (vao, vbo, ibo) is rendering the whole buffer, not individual glyphs
    model_end();

    texture_unbind();
    shader_unbind();
}

Can someone tell me what I'm doing wrong, and, how I'm suposed to render correctly text, with modern OpenGL, with textures and buffers, in order to read the .ttf file and create the necessary information with stb_truetype.h and, then, render the text?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ That output looks like you're reading pixel colour data from a chunk of memory that has nothing to do with your font texture, or with a data stride that's drastically mismatched. You may want to double-check that the data size, texture dimensions, format, and stride of your texture all match what your code expects. \$\endgroup\$
    – DMGregory
    Aug 14 '20 at 22:05
  • \$\begingroup\$ How may I do that? \$\endgroup\$ Aug 14 '20 at 22:41
0
\$\begingroup\$

I'm not an OpenGL expert and since you didn't give us a working example I can't say much about your code but here is an example with a little game library (noengine) that I'm working on. and here is my draw function and here is my shader. I hope it helps.

/*
# run me with 'bash stbttHello.c build'
if [ "$1" == "xterm-send" ]; then xterm-send bash $0 $2 ; exit 1 ; fi
echo -ne "\033c"

sdir=`dirname $0`
odir=${sdir}/../bin
ofilename=`basename $0 .${0##*.}`
ofilepath=${odir}/${ofilename}

mkdir -p $odir
cd $odir

build="gcc $0 -o $ofilepath -I$sdir/../src -I/home/raoof/git/stb -lm -lSDL2 -lGL  -fmax-errors=5"
build="tcc $0 -o $ofilepath -I$sdir/../src -I/home/raoof/git/stb -lm -lSDL2 -lGL  -fmax-errors=5 -DSDL_DISABLE_IMMINTRIN_H"

cmd=$1
echo $cmd...
if [ "$cmd" == "build" ]; then
time $build &&
$ofilepath ; echo $?
elif [ "$cmd" == "debug_build" ]; then
time $build -g &&
gdb -ex r -ex="bt" -ex="set confirm off" -ex=quit $ofilepath
fi
exit 1
*/

#define NOENGINE_IMPLEMENTATION
#include "noengine.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"

unsigned char ttf_buffer[1<<20];
unsigned char temp_bitmap[40][199];
unsigned char textureData[4*40*199];
int textureWidth = 199;
int textureHeight = 40;
int fontSize = 32;

stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
GLuint ftex;

ObjectT image;
float vertexData[12] = {0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1};
stbtt_fontinfo font;

void init() {}

void ginit() {
  fread(ttf_buffer, 1, 1<<20, fopen("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "rb"));
  stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));

  int i,j,ascent,baseline,ch=0;
  float scale, xpos=2; // leave a little padding in case the character extends left
  char *text = "Hello world!"; // intentionally misspelled to show 'lj' brokenness

  scale = stbtt_ScaleForPixelHeight(&font, fontSize);
  stbtt_GetFontVMetrics(&font, &ascent,0,0);
  baseline = (int) (ascent*scale);

  while (text[ch]) {
     int advance,lsb,x0,y0,x1,y1;
     float x_shift = xpos - (float) floor(xpos);
     stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
     stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
     stbtt_MakeCodepointBitmapSubpixel(&font, &temp_bitmap[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, textureWidth, scale,scale,x_shift,0, text[ch]);
     xpos += (advance * scale);
     ++ch;
  }

  for(size_t i = 0 ; i < textureWidth ; i += 1) {
    for(size_t j = 0 ; j < textureHeight ; j += 1) {
      textureData[4 * (j * textureWidth + i) + 0] = 255;
      textureData[4 * (j * textureWidth + i) + 1] = 255;
      textureData[4 * (j * textureWidth + i) + 2] = 255;
      textureData[4 * (j * textureWidth + i) + 3] = temp_bitmap[j][i];
    }
  }
  ObjectT_init(&image);  
  image.textureWidth = textureWidth;
  image.textureHeight = textureHeight;
  image.textureBytesPerPixel = 4;
  image.vertexData = vertexData;
  image.vertexDataLength = sizeof(vertexData) / sizeof(float);
  image.textureCoordinateData = vertexData;
  image.textureCoordinateDataLength = image.vertexDataLength;
  image.textureData = textureData;
  image.transform.size = (Vector) { textureWidth, textureHeight, 0 };
  ObjectT_update(&image);
}

void output() {
  ObjectT_draw(&image);
}

void input() {
}
```
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.