使用openGL ES 2.0和其他线条绘制function的字体渲染(Freetype)不起作用

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

我有一个组合字体渲染和使用此function的问题,如下所示:

// 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()绘制连接提供的顶点的一系列线。 Flags CLEAR,RENDER和UPDATE被用来让我做类似的事情:

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

我使用字体渲染function做了同样的事情,因此我可以在各种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 eg 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); 

事实certificate,我可以在各种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 | UPDATE。

问题:

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

主要问题是,当drawVertices()绘制线条时,用于渲染文本的着色器程序仍处于活动状态(当前渲染状态drawVertices() 。 对于此着色器程序,纹理坐标和纹理是必需的。

在绘制线条时,您既不提供任何纹理坐标,也不会在渲染线条时将纹理与字体的字形一起使用。

创建第二个非常简单的着色器,您可以使用它来绘制线条:

顶点着色器:shader_line.vert

 #version 100 attribute vec2 coord; void main(void) { gl_Position = vec4(coord.xy, 0, 1); } 

片段着色器:shader_line.frag

 #version 100 #ifdef GL_ES precision highp float; #endif uniform vec4 color; void main(void) { gl_FragColor = color; } 

创建着色器程序:

 GLuint shaderProg_line = shaderProgLoad("shader_line.vert", "shader_line.frag"); GLuint line_color_loc = glGetUniformLocation( shaderProg_line, "color" ); 

在渲染文本之后和渲染线之前,将当前程序从“文本”着色器程序切换到“线”着色器程序:

 lUseProgram( shaderProg ); // "text" shader glRenderText(tmp, 0, 0, Olive, CLEAR | RENDER); ..... glUseProgram( shaderProg_line ); // "line" shader float col[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; glUniform4fv( line_color_loc, 1, col ); // set uniform "color" of the "line" shader drawVertices(window, vertices, sizeOfVertices, RENDER); ..... 

我能够通过重置几乎所有两个操作不常见的东西来解决这个问题(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); 用于在绘制线条时禁用混合。 最重要的是,每次调用drawVertices()时,我都会使用glBindBuffer()设置适当的缓冲区以供opengl使用。 当相应的对象名称为0时,glGenBuffers()仅使用一次,这意味着尚未将已使用的对象名称分配给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 eg 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; } 

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

我还没有测试我的raspi3中的function。 我会尽快回来更新。 无论如何,上面给出的function完全正常。

感谢你的宝贵时间。