c - 使用openGL ES 2.0结合其他画线功能进行字体渲染(Freetype)不起作用

标签 c fonts opengl-es-2.0 sdl-2 freetype

此线程与 https://stackoverflow.com/questions/50955558/render-fonts-with-sdl2-opengl-es-2-0-glsl-1-0-freetype 相关

我在组合字体渲染和使用此函数时遇到问题,如下所示:

// 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;
}

该函数使用 glDrawArrays() 绘制一系列连接所提供顶点的线。标志 CLEAR、RENDER 和 UPDATE 被用来让我做类似的事情:

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

我对字体渲染函数做了同样的事情,从而使我能够在不同的 x,y 坐标中绘制多个字符串。接下来的两个函数根据我首先提交的代码以及您的更正进行字体渲染。

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

因此我可以这样做:

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

事实证明,我可以在不同的 x,y 坐标处渲染字体,也可以使用函数 drawVertices 来渲染连接这些顶点的线,但不能同时渲染两者。也就是说,我不能这样做:

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

正如您所知,逻辑是在任一函数中您都必须使用 CLEAR | RENDER 标志,然后仅执行 RENDER,并在最后一次调用任一函数时使用 RENDER |更新。

问题:

(1) 在我尝试执行前一项操作时,即组合 glRenderText() + drawVertices() 时,我失败了,因为在依次调用它们之前显然需要设置一些东西。

(2)我面临的另一个问题是,在我的raspi3上运行代码导致drawVertices()工作正常,当涉及到字体时,我只能看到glClearColor()和glClear(GL_COLOR_BUFFER_BIT)的效果,这意味着通过 glClearColor() 设置的颜色清除了显示,但没有看到字体渲染。我尝试了两种 GL 驱动模式。有一种称为 FULL KMS GL 驱动程序,另一种称为 FAKE KMS GL 驱动程序。

此外,为了使 drawVertices() 工作,我必须注释下面提供的代码:

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

我仍然必须保持以下代码处于事件状态:

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

最佳答案

我能够通过重置两个操作(glRenderText()和drawVertices())中不常见的几乎所有内容来解决该问题 以下代码在调用两个函数 ()glRenderText() 和 drawVertices()) 中的任何一个之前保持不变。这两个函数已更新,以便在到达执行 glDrawArrays() 的点之前完成适当的重置

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

这是更新后的函数,它会重置一些选项,以便我们得到我们需要的结果。例如,glDisable(GL_BLEND);用于在绘制线条时禁用混合。最重要的当然是我使用 glBindBuffer() 设置适当的缓冲区,以便每次调用 drawVertices() 时 opengl 使用。 glGenBuffers() 仅在对应的对象名称为 0 时使用一次,这意味着所使用的对象名称尚未分配给 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;
}

函数 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;
}

其逻辑是,在 main() 或全局范围内定义 2 个 vbo(一个用于通过 drawVertices 绘制线条,一个用于通过 glRenderText() 绘制字体),并传递给 glRenderText() 和 drawVertices() 。这两个函数更新其本地副本的值并返回 vbo,以便更新 main 或全局范围中的 vbo。当然,这可以通过完全通过引用传递它们来完成,而不是采用我的方法。

不过我还没有测试我的 raspi3 中的功能。我很快就会回来更新。无论如何,上面给出的功能都是完整的。

再次感谢您的宝贵时间。

关于c - 使用openGL ES 2.0结合其他画线功能进行字体渲染(Freetype)不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50982640/

相关文章:

java - 在 Java : java. awt.Font 中渲染字体行为不正确

ios - iOS上的OpenGL ES 2.0:在没有法线的情况下从zBrush导出导入.OBJ文件

android - 相机 View 上的 opengl 叠加

c++ - 没有分号的程序在 C 中编译得很好,而不是在 C++ 中为什么?

c - fclose 是否立即释放文件?

html - 浏览器中的字体渲染不一致

android opengl 纹理重叠

c - C 中最简单的 Arraylist 实现

c - IPC_CREAT 有什么用? 0666 C 中 shmget() 函数中的标志

css - 从谷歌安装的字体看起来与通过他们的 API 获得的字体不同?