Font rendering (Freetype) with openGL ES 2.0 combined with other line drawing functions does not work

左心房为你撑大大i 提交于 2020-01-06 06:25:27

问题


This thread is related to https: //stackoverflow.com/questions/50955558/render-fonts-with-sdl2-opengl-es-2-0-glsl-1-0-freetype

I have a problem combining font rendering and using this function as follows:

// Create VBO (Vertex Buffer Object) based on the vertices provided, render the vertices on the 
// background buffer and eventually swap buffers to update the display.
// Return index of VBO buffer
GLuint drawVertices(SDL_Window *window, Vertex *vertices, GLsizei numVertices, int mode){

  // Number of vertices elements must be provided as a param (see numVertices) because 
  // sizeof() cannot calculate the size of the type a pointer points to  
  //GLsizei vertSize = sizeof(vertices[0]);

  //SDL_Log("Vertices size is %d, no of vertices is %d", vertSize, numVertices);

  // Create a VBO (Vertex Buffer Object)
  GLuint VBO = vboCreate(vertices, numVertices);

  if (!VBO) {
    // Failed. Error message has already been printed, so just quit
    return (GLuint)NULL;
  }

  // Set up for rendering the triangle (activate the VBO)
  GLuint positionIdx = 0; // Position is vertex attribute 0
  glBindBuffer(GL_ARRAY_BUFFER, VBO);
  glVertexAttribPointer(positionIdx, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)0);
  glEnableVertexAttribArray(positionIdx);

  if (mode & CLEAR){
    // Set color of the clear operation
    glClearColor(0.0, 0.0, 0.0, 1.0);
    // Clears the invisible buffer
    glClear(GL_COLOR_BUFFER_BIT);
  }

  // Now draw!
  //  GL_POINTS = Draw only the pixels that correspond to the vertices coordinates
  //  GL_LINE_STRIP = Draw line that connects the vertices coordinates
  //  GL_LINE_LOOP = Draw line that connects the vertices coordinates plus a line that re-connects the last coordinate with the first
  if (mode & RENDER){ glDrawArrays(GL_LINE_STRIP, 0, numVertices); }

  // Don’t forget to flip the buffers as well, to display the final image:
  // Update the window
  if (mode & UPDATE){ SDL_GL_SwapWindow(window); }

  return VBO;
}

This function uses glDrawArrays() to draw a series of lines connecting the provided vertices. Flags CLEAR, RENDER & UPDATE are being used to let me do something like:

drawVertices(window, vertices, sizeOfVertices, CLEAR | RENDER);
drawVertices(window, vertices, sizeOfVertices, RENDER);
drawVertices(window, vertices, sizeOfVertices, RENDER | UPDATE);

I did the same thing with the font rendering function thus enabling me to draw multiple strings in various x,y coordinates. The next two functions do the font rendering based on the code i submitted at the first place and off course your corrections.

void render_text(const char *text, float x, float y, float sx, float sy) {
  const char *p;

  FT_GlyphSlot g = face->glyph;

  SDL_Log("Debug info: glyph w: %d, glyph rows: %d", g->bitmap.width, g->bitmap.rows);

  for(p = text; *p; p++) {

    // If FT_Load_Char() returns a non-zero value then the glyph in *p could not be loaded
    if(FT_Load_Char(face, *p, FT_LOAD_RENDER)){ continue; }

    glTexImage2D(
      GL_TEXTURE_2D,
      0,
      GL_RED,
      g->bitmap.width,
      g->bitmap.rows,
      0,
      GL_RED,
      GL_UNSIGNED_BYTE,
      g->bitmap.buffer
    );

    float x2 = x + g->bitmap_left * sx;
    float y2 = -y - g->bitmap_top * sy;
    float w = g->bitmap.width * sx;
    float h = g->bitmap.rows * sy;

    GLfloat box[4][4] = {
        {x2,     -y2    , 0, 0},
        {x2 + w, -y2    , 1, 0},
        {x2,     -y2 - h, 0, 1},
        {x2 + w, -y2 - h, 1, 1},
    };

    glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    x += (g->advance.x>>6) * sx;
    y += (g->advance.y>>6) * sy;
  }
}


void glRenderText(char *text, int _x, int _y, SDL_Color rgb, int mode) {

  float x = _x;
  float y = _y;

  int w=0, h=0;

  SDL_GetWindowSize(SDLmain.window, &w, &h);
  float xMax = 2.0 / (float)w;
  float yMax = 2.0 / (float)h;

  GLuint color_loc = glGetUniformLocation( shaderProg, "color" );
  float col[4] = { (float)rgb.r/255, (float)rgb.g/255, (float)rgb.b/255, 1 }; // red and opaque
  glUniform4fv( color_loc, 1, col); 

  // Clear invisible buffer
  if (mode & CLEAR){ glClearColor(0.0, 0.0, 0.0, 1); glClear(GL_COLOR_BUFFER_BIT); }

  // If coordinate system required is:
  // COORD_SYS_CONVENTIONAL = (1) left bottom corner is 0,0 and moving towards right and top sides we reach max screen size in pixels e.g 1024 * 768 pixels
  // COORD_SYS_CARTECIAN = (2) left bottom corner is -1,-1 and moving towards right and top sides we reach +1,+1 . The center of the display is always 0,0
  if (mode & ~COORD_SYS_CARTECIAN){
    x = (_x * xMax)-1;
    y = (_y * yMax)-1;
  }

  // Draw the text on the invisible buffer
  if (mode & RENDER){ render_text(text, x, y, xMax, yMax); }

  // Update display
  if (mode & UPDATE){ SDL_GL_SwapWindow(SDLmain.window); }
}

I therefore can do:

  glRenderText(tmp, 0, 0, Olive, CLEAR | RENDER | UPDATE);
  glRenderText(tmp, 0, 150, Yellow_Green, RENDER);
  glRenderText(tmp, 0, 300, Light_Coral, RENDER | UPDATE);

It turns out that i can either render fonts at various x,y coordinates or use the function drawVertices to render lines connecting those vertices BUT not both. That is, i cannot do this:

  glRenderText(tmp, 0, 0, Olive, CLEAR | RENDER);
  glRenderText(tmp, 0, 150, Yellow_Green, RENDER);
  glRenderText(tmp, 0, 300, Light_Coral, RENDER);

  drawVertices(window, vertices, sizeOfVertices, RENDER);
  drawVertices(window, vertices, sizeOfVertices, RENDER);
  drawVertices(window, vertices, sizeOfVertices, RENDER | UPDATE);

As you can tell the logic is that in either functions you have to just with a CLEAR | RENDER flag, then do only RENDER and on your last call to either functions use RENDER | UPDATE.

ISSUES:

(1) In my attempt to do the previous, that is, combinning glRenderText() + drawVertices() i failed because there is clearly something to be setup prior calling them one after the other.

(2) Another issue that i am facing is that running the code on my raspi3 resulted in drawVertices() working fine back when it comes to fonts i could only see the effect of glClearColor() & glClear(GL_COLOR_BUFFER_BIT) which means that the display was cleared with the color setup by glClearColor() but there was no font rendering to be seen. I tried both GL driver mode. There is one called FULL KMS GL driver and another called FAKE KMS GL driver.

Also, in order for the drawVertices() to work i had to comment the code provided below:

  FT_Set_Pixel_Sizes(face, 0, 200);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  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);

  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  GLuint vbo;
  GLuint attribute_coord=0;

  glGenBuffers(1, &vbo);
  glEnableVertexAttribArray(attribute_coord);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);

I still had to keep the following code active:

  // Load the shader program and set it for use
  shaderProg = shaderProgLoad("shaderV1.vert", "shaderV1.frag");

  GLuint tex_loc   = glGetUniformLocation( shaderProg, "tex" );
  GLuint color_loc = glGetUniformLocation( shaderProg, "color" );

  // Activate the resulting shaders program
  glUseProgram(shaderProg);


  glUniform1i( tex_loc, 0 ); // 0, because the texture is bound to of texture unit 0
  // Define RGB color + Alpha
  float col[4] = { 0.0f, 1.0f, 1.0, 1.0f };
  glUniform4fv( color_loc, 1, col);

回答1:


i was able to resolve the issue by reseting pretty much everything that is not common to the two operations (glRenderText() & drawVertices()) The following code stays as is before calling any of the two functions ()glRenderText() & drawVertices()). These two functions have been updated so that a proper reset is done before reaching the point where glDrawArrays() is executed

// Load the shader program and set it for use
shaderProg = shaderProgLoad("shaderV1.vert", "shaderV1.frag");

// Activate the resulting shaders program
glUseProgram(shaderProg);

// After the shaders (vertex & fragment) have been compiled & linked into a program
// we can query the location index value of a uniform variable existing in the program.
// In this case we are querying uniform variables "tex" that exist in the fragment shader 
GLuint tex_loc   = glGetUniformLocation( shaderProg, "tex" );
// Set the value of the uniform variable "tex_loc" to 0, because the texture is bound to of texture unit 0
glUniform1i( tex_loc, 0 );

This is the updated function that resets some options so that we get the result we need. For example, glDisable(GL_BLEND); is used to disable blending when it comes to drawing lines. The most important off course is that i use glBindBuffer() to set the appropriate buffer for use by opengl every time drawVertices() is called. glGenBuffers() is used only once when the corresponding object name is 0 meaning that an used object name has not yet been assigned to the vbo.

GLuint drawVertices(SDL_Window *window, GLuint vbo, Vertex *vertices, GLsizei numVertices, SDL_Color rgb, int mode){

  float col[4] = { (float)rgb.r/255, (float)rgb.g/255, (float)rgb.b/255, 1.0 };

  // Get an available object name for glBindBuffer() when object name is ZERO
  if (!vbo){ glGenBuffers(1, &vbo); }

  // Check for problems
  GLenum err = glGetError();

  // Deal with errors
  if (err != GL_NO_ERROR) {
    // Failed
    glDeleteBuffers(1, &vbo);
    SDL_Log("Creating VBO failed, code %u\n", err);
    vbo = 0;
  }
  else if (!vbo) {
    // Failed. Error message has already been printed, so just quit
    return (GLuint)NULL;
  }

  if (mode & CLEAR){
    // Set color of the clear operation
    glClearColor(0.0, 0.0, 0.0, 1.0);
    // Clears the invisible buffer
    glClear(GL_COLOR_BUFFER_BIT);
  }

  if (mode & RENDER){
    // Dissable blending when drawing lines
    glDisable(GL_BLEND);
    // Set up for rendering the triangle (activate the vbo)
    // Position is vertex attribute 0
    GLuint attribute_coord = 0; 
    // Specifies the index of the generic vertex attribute and enables it
    glEnableVertexAttribArray(attribute_coord);
    // Set the buffer to be used from now on
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    // Define an array of generic vertex attribute data
    glVertexAttribPointer(attribute_coord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)0);
    // Get the location of the uniform variable "color_loc"
    GLuint color_loc = glGetUniformLocation( shaderProg, "color" );  
    // Set the value of the uniform variable "color_loc" to array "col"
    glUniform4fv( color_loc, 1, col);
    // Copy vertices into buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices, GL_STATIC_DRAW);

    // Now draw!
    //  GL_POINTS = Draw only the pixels that correspond to the vertices coordinates
    //  GL_LINE_STRIP = Draw line that connects the vertices coordinates
    //  GL_LINE_LOOP = Draw line that connects the vertices coordinates plus a line that re-connects the last coordinate with the first
    //  GL_TRIANGLE_FAN = 
    glDrawArrays(GL_LINE_STRIP, 0, numVertices);
  }

  // Don’t forget to flip the buffers as well, to display the final image:
  // Update the window
  if (mode & UPDATE){ SDL_GL_SwapWindow(window); }

  return vbo;
}

Things work pretty much the same way for function glRenderText()

// render_text is called by glRenderText()
void render_text(const char *text, float x, float y, float sx, float sy) {
  const char *p;

  FT_GlyphSlot g = face->glyph;

  //SDL_Log("Debug info: glyph w: %d, glyph rows: %d", g->bitmap.width, g->bitmap.rows);

  for(p = text; *p; p++) {

    // If FT_Load_Char() returns a non-zero value then the glyph in *p could not be loaded
    if(FT_Load_Char(face, *p, FT_LOAD_RENDER)){ continue; }

    glTexImage2D(
      GL_TEXTURE_2D,
      0,
      GL_RED,
      g->bitmap.width,
      g->bitmap.rows,
      0,
      GL_RED,
      GL_UNSIGNED_BYTE,
      g->bitmap.buffer
    );

    float x2 = x + g->bitmap_left * sx;
    float y2 = -y - g->bitmap_top * sy;
    float w = g->bitmap.width * sx;
    float h = g->bitmap.rows * sy;

    GLfloat box[4][4] = {
        {x2,     -y2    , 0, 0},
        {x2 + w, -y2    , 1, 0},
        {x2,     -y2 - h, 0, 1},
        {x2 + w, -y2 - h, 1, 1},
    };

    glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    x += (g->advance.x>>6) * sx;
    y += (g->advance.y>>6) * sy;
  }
}


GLuint glRenderText(char *text, int fontSize, GLuint vbo, int _x, int _y, SDL_Color rgb, int mode) {

  float x = _x;
  float y = _y;

  float xMax = 2.0 / (float)getWindowWidth();
  float yMax = 2.0 / (float)getWindowHeight();
  GLuint attribute_coord=0;

  float col[4] = { (float)rgb.r/255, (float)rgb.g/255, (float)rgb.b/255, 1 };

  // Enable blending when drawing fonts
  glEnable(GL_BLEND);

  // Set the W & H of the font loaded
  FT_Set_Pixel_Sizes(face, 0, fontSize);

  // If vbo is ZERO setup and get an object name      
  if (!vbo){
    // Enables blending operations
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Set texture parameters
    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);

    // Specifies the alignment requirements for the start of each pixel row in memory
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Save into vbo one unused buffer name (index) for use with glBindBuffer
    glGenBuffers(1, &vbo);
    // Specifies the index of the generic vertex attribute and enables it
    glEnableVertexAttribArray(attribute_coord);
  }    

  // Set the buffer to be used from now on to the one indicated by vbo
  glBindBuffer(GL_ARRAY_BUFFER, vbo);

  // Define an array of generic vertex attribute data
  glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);  

  GLuint color_loc = glGetUniformLocation( shaderProg, "color" );

  // Set the value of the uniform variable "color_loc" from array "col"
  glUniform4fv( color_loc, 1, col); 

  // Clear invisible buffer
  if (mode & CLEAR){
    glClearColor(0.0, 0.0, 0.0, 1); 
    glClear(GL_COLOR_BUFFER_BIT); 
  }

  // If coordinate system required is:
  // COORD_SYS_CONVENTIONAL = (1) left bottom corner is 0,0 and moving towards right and top sides we reach max screen size in pixels e.g 1024 * 768 pixels
  // COORD_SYS_CARTECIAN = (2) left bottom corner is -1,-1 and moving towards right and top sides we reach +1,+1 . The center of the display is always 0,0
  if (mode & ~COORD_SYS_CARTECIAN){
    x = (_x * xMax)-1;
    y = (_y * yMax)-1;
  }

  // Draw the text on the invisible buffer
  if (mode & RENDER){ render_text(text, x, y, xMax, yMax); }

  // Update display
  if (mode & UPDATE){ SDL_GL_SwapWindow(SDLmain.window); }

  return vbo;
}

The logic is that 2 vbos (one for drawing line through drawVertices and one for drawing fonts through glRenderText()) are defined in main() or in the global scope and are passed to glRenderText() & drawVertices() . These two functions update the values of their local copies and return the vbos so that the vbo in main or in the global scope get updated. That off course could be done by passing them entirely by reference instead of adopting my approach.

I have not yet tested the functionality in my raspi3 though. I will get back real soon with an update. Anyway, the functions given above are fully functional.

Thanks again for your time.



来源:https://stackoverflow.com/questions/50982640/font-rendering-freetype-with-opengl-es-2-0-combined-with-other-line-drawing-fu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!