Font rendering onto off-screen FBO not working. (Note: fonts are loaded by creating textures using Freetype lib with glTexImage2D)

人走茶凉 提交于 2020-05-16 06:31:51

问题


I have the following code to render a rectangle and few texts onto an offscreen FBO. And then I try to bind the texture (attached to FBO) in default/ display framebuffer. I am able to render the rectangle but the fonts are not getting rendered. I tried debugging, but so far could not. I have to do this in OpenGL ES 2.0 only.

My rectangle is of orange color. The texts/fonts are of red color. I use Freetype lib and glTexImage2D call to create individual textures for each font. When I render directly on the default framebuffer, I get an orange rectangle and red fonts successfully. But when I do it on offscreen FBO first, I get a reddish rectangle and some junk small texts (I assume). I am continuing debugging, but any input will be helpful.

Note: I am new to OpenGL.

EGLSurface eglsurface;
EGLDisplay egldisplay;
EGLConfig eglconfig;
EGLContext eglcontext;
void* NativeWindow;
GLuint VBO;
FT_Face face;
unsigned int shaderProgram;
unsigned int vertexShader;
unsigned int fragmentShader;
int color_loc;

const GLuint WIDTH = 1920, HEIGHT = 1080;

EGLint Attributes[] = { EGL_RED_SIZE,        1,
                        EGL_GREEN_SIZE,      1,
                        EGL_BLUE_SIZE,       1,
                        EGL_ALPHA_SIZE,      1,
                        EGL_NONE };

EGLint contextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION,
                               2,
                               EGL_NONE };

void GlInit()
{
  glViewport(0, 0, WIDTH, HEIGHT);

  const char *vertexShaderSource = 
    "attribute vec4 vertex;\n"
    "varying vec2 texcoord;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(vertex.xy, 0, 1);\n"
    "texcoord = vertex.zw;\n"
    "}\n";

  const char *fragmentShaderSource =
    "precision highp float;\n"
     "varying vec2 texcoord;\n"
    "uniform sampler2D s_texture;\n"
    "uniform vec4 myColor;\n"
    "void main(void) {\n"
    "gl_FragColor = vec4(1, 1, 1, texture2D(s_texture, texcoord).a) * myColor;\n"
    "}\n";

  unsigned int vertexShader;
  vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
  glCompileShader(vertexShader);
  int  success;
  char infoLog[512];
  glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  if(!success) {
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n");
  }

  fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
  glCompileShader(fragmentShader);
  glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  if(!success) {
    glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n");
  }

  shaderProgram = glCreateProgram();
  glAttachShader(shaderProgram, vertexShader);
  glAttachShader(shaderProgram, fragmentShader);
  glLinkProgram(shaderProgram); 
  glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
  if(!success) {
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    printf("ERROR::SHADER::LINKING_FAILED\n");
  }

  glGenBuffers(1, &VBO);
  glUseProgram(shaderProgram);

  //glClearDepthf(1.0f);
  //glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  //glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // SAGAR - Blend required for texture
  //glEnable(GL_BLEND);
  //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  // Get the color location in fragment shader, will fill on need
  color_loc = glGetUniformLocation(shaderProgram, "myColor");
}

void DrawRect()
{
  glBindBuffer(GL_ARRAY_BUFFER, VBO);

  int position_loc = glGetAttribLocation(shaderProgram, "vertex");
  glEnableVertexAttribArray(position_loc);
  glVertexAttribPointer(position_loc, 4, GL_FLOAT, GL_FALSE, 0, 0);

  GLfloat rectangle[4][4] = {
    -0.5f,  0.5f, 0.0f, 1.0f,
     0.5f,  0.5f, 0.0f, 1.0f,
    -0.5f, -0.5f, 0.0f, 1.0f,
     0.5f, -0.5f, 0.0f, 1.0f
  };

  glUniform4f(color_loc, 1.0f, 0.5f, 0.2f, 1.0f); // Orange

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

void render_fboTexture()
{
  int TextureLocation = glGetUniformLocation(shaderProgram, "s_texture");
  glUniform1i(TextureLocation, 0);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  // Draw in a small quad for testing
  GLfloat quad[4][4] = {
    {-0.5, 0.5, 0, 0},
    {0.5, 0.5, 1, 0},
    {-0.5, -0.5, 0, 1},
    {0.5, -0.5, 1, 1},
  };

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

void render_captiondata(const char* caption, float x, float y, float sx, float sy);
void RenderTexture()
{
    int TextureLocation = glGetUniformLocation(shaderProgram, "s_texture");
    glUniform1i(TextureLocation, 0);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Set desired text color
    glUniform4f(color_loc, 0.5f, 0.0f, 0.0f, 1.0f); // Red

    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
      printf("SAGAR- Could not init FreeType Library\n");
    }

    if (FT_New_Face(ft, "./xyz.ttf", 0, &face)) {
      printf("SAGAR - Failed to load font\n");
    }
    FT_Set_Pixel_Sizes(face, 0, 48);

    GLuint texture;
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

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

    // Calculate the scales both sides
    float sx = 2.0 / WIDTH;
    float sy = 2.0 / HEIGHT;
    float x = -0.4;
    float y = 0.3;

    render_captiondata("-- Hello OpenGL! --", x, y, sx, sy);
}

void render_captiondata(const char* caption, float x, float y, float sx, float sy)
{
  const char *p;
  for(p = caption; *p; p++) {
    if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) {
        printf("SAGAR - Failed to load Glyph\n");
    }

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

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

    glTexImage2D(
      GL_TEXTURE_2D, 0, GL_ALPHA, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0,
      GL_ALPHA, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);

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

    x += (face->glyph->advance.x/64) * sx;
    y += (face->glyph->advance.y/64) * sy;
  }
}

int main()
{
  EglInit();
  GlInit();

  // FBO experiment: SAGAR
  GLuint fbo;
  glGenFramebuffers(1, &fbo);
  glBindFramebuffer(GL_FRAMEBUFFER, fbo);

  GLuint texture;
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  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);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
  glBindTexture(GL_TEXTURE_2D, 0);

  GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  if (status != GL_FRAMEBUFFER_COMPLETE) {
    printf("Problem with OpenGL framebuffer : %x\n", status);
  }

  DrawRect();
  RenderTexture(); // Does not work
  glBindTexture(GL_TEXTURE_2D, 0);
  eglSwapBuffers(egldisplay, eglsurface);

  glBindFramebuffer(GL_FRAMEBUFFER, 0);
  glBindTexture(GL_TEXTURE_2D, texture);
  glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  render_fboTexture();
  glBindTexture(GL_TEXTURE_2D, 0);

  //RenderTexture(); // Directly onto default/main framebuffer works
  eglSwapBuffers(egldisplay, eglsurface);
  sleep(10);

  glDeleteShader(vertexShader);
  glDeleteShader(fragmentShader);
  glDeleteBuffers(1, &VBO);
  glDeleteProgram(shaderProgram);

  return 0;
}

egl related init.

void EglInit()
{   
  EGLint configCount;
  egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  if (egldisplay == EGL_NO_DISPLAY) {
    printf("eglGetDisplay() failed: %d\n", eglGetError());
  }

  EGLint majorVersion = 0;
  EGLint minorVersion = 0;
  if (!eglInitialize(egldisplay, &majorVersion, &minorVersion)) {
   printf("eglInitialize() failed: %d\n", eglGetError());
  }

  if (!eglChooseConfig(egldisplay, Attributes, &eglconfig, 1, &configCount))
  {
    printf("eglChooseConfig failed : %d\n", eglGetError());
  }

  eglcontext = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT, contextAttributes);
  if (eglcontext == EGL_NO_CONTEXT) {
    printf("eglCreateContext() failed\n");
  }

  eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, NativeWindow, NULL);
  if (eglsurface == EGL_NO_SURFACE) {
    printf("eglCreateWindowSurface() failed\n");
  }

  eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext);
}

回答1:


There are some issue.

Enable Blending when you render the text:

DrawRect();

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
RenderTexture();
glDisable(GL_BLEND);

But the major issue is the fragment shader. The fragment shader takes the alpha channel of the texture and a color form a uniform.

gl_FragColor = vec4(1, 1, 1, texture2D(s_texture, texcoord).a) * myColor;

This works fine for rendering the quad and the text. Note, the glyphs are stored in textures, where the red green and blue color is zero and the alpha channel contains the glyph mask.

You use the same shader, to blit the framebuffer, that won't work at all, because for copying the framebuffer you would need a shader that reads the colors from the texture (your shader gets it from the uniform). e.g.:

gl_FragColor = texture2D(s_texture, texcoord);

If you want to use 1 shader for all the drawing, then create a fragment shader, which uses the alpha channel of myColor, to mix the color channels of the texture and myColor. If it is 1, then the colors are read from myColor, if ti is 0.0, then the colors are read from the texture:

vec4 texColor   = texture2D(s_texture, texcoord);
vec3 finalColor = mix(texColor.rgb, myColor.rgb, myColor.a);
gl_FragColor    = vec4(finalColor, texColor.a);

Set the colors in DrawRect and RenderTexture:

void DrawRect()
{
    // [...]

    glUniform4f(color_loc, 1.0f, 0.5f, 0.2f, 1.0f); // Orange
void RenderTexture()
{
    // [...]

    glUniform4f(color_loc, 0.5f, 0.0f, 0.0f, 1.0f); // Red

But set an alpha channel of 0.0 in render_fboTexture:

void render_fboTexture()
{
    // [...]

    glUniform4f(color_loc, 1.0f, 1.0f, 1.0f, 0.0f); // use texture
} 

Furthermore the y component of your texture coordinates for the quad in render_fboTexture is flipped. Change the texture coordinates:

void render_fboTexture()
{
    // [...]

    GLfloat quad[4][4] = {
        {-1.0,  1.0,  0, 1},
        { 1.0,  1.0,  1, 1},
        {-1.0, -1.0,  0, 0},
        { 1.0, -1.0,  1, 0},

    // [...]
};


来源:https://stackoverflow.com/questions/61475832/font-rendering-onto-off-screen-fbo-not-working-note-fonts-are-loaded-by-creat

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