2

Following a tutorial, I'm trying to render text in OpenGL using FreeType. As such, a grayscale 8-bit image is used as texture for each character in a way that each byte of the image corresponds to the texture's red component.

In order to render the text in other colors, it's suggested that you use a Shader. However, when using the provided shader, instead of seeing a colored letter, I see a colored box, as if there was no texture involved at all.

Here's how it looks like with no Shader:

img

And here's how it looks with the Shader:

img

(the box also gets its position shifted)

Here's the Vertex Shader code:

#version 330 core
layout (location = 0) in vec4 vertex;
out vec2 TexCoords;
void main() {
    gl_Position = vec4(vertex.xy, 0.0, 1.0);
    TexCoords = vertex.zw;
}

Here's the Fragment Shader code:

#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec4 textColor;
void main() {
    vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
    color = textColor * sampled;
}

And here's the code that renders on the screen:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Shader here
glUseProgram(shaderProgram);
glUniform4f(
    glGetUniformLocation(shaderProgram, "textColor"),
    0.0f, 1.0f, 0.0f, 1.0f
);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texID);
glUniform1i(glGetUniformLocation(shaderProgram, "text"), 0);

// Draw here
glBegin(GL_QUADS);
glTexCoord2f(1., 0.);
glVertex2f(20. / WIDTH, 20. / HEIGHT);

glTexCoord2f(0., 0.);
glVertex2f(100. / WIDTH, 20. / HEIGHT);

glTexCoord2f(0., 1.);
glVertex2f(100. / WIDTH, 100. / HEIGHT);

glTexCoord2f(1., 1.);
glVertex2f(20. / WIDTH, 100. / HEIGHT);
glEnd();

And, also, here's the whole code:

#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype2/freetype/freetype.h>

const unsigned int WIDTH = 640;
const unsigned int HEIGHT= 480;

const char *vertex_shader_src =
"#version 330 core\n"
"layout (location = 0) in vec4 vertex;\n"
"out vec2 TexCoords;\n"
"void main() {\n"
"gl_Position = vec4(vertex.xy, 0.0, 1.0);\n"
"TexCoords = vertex.zw;}\0";

const char *frag_shader_src = 
"#version 330 core\n"
"in vec2 TexCoords;\n"
"out vec4 color;\n"
"uniform sampler2D text;\n"
"uniform vec4 textColor;\n"
"void main() {\n"
"vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n"
"color = textColor * sampled;}\0";

int main() {

    // -------------------------------------------------------------------------
    // WINDOW INITIALIZATION

    GLFWwindow *window;

    glfwInit();
    window = glfwCreateWindow(640, 480, "SHADER", NULL, NULL);
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);

    glClearColor(0., 0. , 0., 1.);
    glColor4f(1., 1., 1., 1.);
    glPointSize(1.);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, (WIDTH*1.)/HEIGHT, 1., 0, 1., -1.);
    glViewport(0, 0, WIDTH, HEIGHT);
    // -------------------------------------------------------------------------
    // SHADER INITIALIZATION

    glewInit();

    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertex_shader_src, NULL);
    glCompileShader(vertexShader);

    int fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, &frag_shader_src, NULL);
    glCompileShader(fragShader);

    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragShader);
    // -------------------------------------------------------------------------
    // TEXTURE FOR A SINGLE CHARACTER
    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
        printf("Error in FT_Init_FreeType\n");
        return 1;
    }

    FT_Face face;
    if (FT_New_Face(ft, "Ubuntu-R.ttf", 0, &face)) {
        printf("Error in FT_New_Face\n");
        return 1;
    }

    FT_Set_Pixel_Sizes(face, 0, 96);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    if (FT_Load_Char(face, 'A', FT_LOAD_RENDER)) {
        printf("Error in FT_Load_Char\n");
        return 1;
    }

    unsigned int texID;
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);

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

    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);
    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    // -------------------------------------------------------------------------
    // MAIN LOOP

    while (!glfwWindowShouldClose(window)) {

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // Shader here
        glUseProgram(shaderProgram);
        glUniform4f(
            glGetUniformLocation(shaderProgram, "textColor"),
            0.0f, 1.0f, 0.0f, 1.0f
        );

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texID);
        glUniform1i(glGetUniformLocation(shaderProgram, "text"), 0);

        // Draw here
        glBegin(GL_QUADS);
        glTexCoord2f(1., 0.);
        glVertex2f(20. / WIDTH, 20. / HEIGHT);

        glTexCoord2f(0., 0.);
        glVertex2f(100. / WIDTH, 20. / HEIGHT);

        glTexCoord2f(0., 1.);
        glVertex2f(100. / WIDTH, 100. / HEIGHT);

        glTexCoord2f(1., 1.);
        glVertex2f(20. / WIDTH, 100. / HEIGHT);
        glEnd();
        glUseProgram(0);

        glFlush();
        glfwSwapBuffers(window);
        glfwPollEvents();

    }

    glDeleteProgram(shaderProgram);
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;

}

I suspect the problem is either because I'm not actually sending the texture to the shader or the shader is wrong.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Syndelis
  • 23
  • 3
  • Where are your `glVertexAttribPointer()` call(s)? Because you aren't using the fixed-function compatibility attributes in your shaders. – genpfault Jul 25 '20 at 23:48
  • I'm sorry, I don't think I understood that last "fixed-function compatibility attributes" part. I'm not sure what that would be, if you care to explain. – Syndelis Jul 26 '20 at 01:56
  • How do you expect `glTexCoord2f` and `glVertex2f` to appear in the `vertex` attribute? These are legacy deprecated fixed-pipeline functions and you should use `glVertexAttribPointer` and `glDrawArrays` instead. – Yakov Galka Jul 26 '20 at 02:58
  • 1
    See section 7.2 "Compatibility Profile Vertex Shader Built-In Inputs" (page 151) in the [OpenGL Shading Language 4.60 Specification](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) for the list of fixed-function vertex attribute variables like `gl_MultiTexCoord0`. – genpfault Jul 26 '20 at 03:39
  • I had no idea those functions were deprecated! Thank you both very much for your time! – Syndelis Jul 26 '20 at 03:45
  • Do you see how you are using the zw part of the vertex position for the texture coordinate, but your vertices don't have a zw part? Also, how should OpenGL know that `vertex` is meant to be the vertex position, anyway? – user253751 Jul 28 '20 at 10:56

1 Answers1

0

You are using Legacy OpenGL OpenGL and you are drawing the geometry by immediate mode glBegin/glEnd sequences. You cannot access the texture coordinates which are set by glTexCoord by a general vertex shader input. See What are the Attribute locations for fixed function pipeline in OpenGL 4.0++ core profile?.
You must downgrade the vertex shader to GLSL version 1.20 (OpenGL Shading Language 1.20 Specification) and you have to access the vertex coordinates by gl_Vertex and the texture coordinates by gl_MultiTexCoord0:

#version 120

varying vec2 TexCoords;

void main() 
{
    gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0);
    TexCoords = gl_MultiTexCoord0.st;
}

If you want to use the shader from the question, then you have to specify the arrays of vertex attributes by glVertexAttribPointer (see Vertex Specification) and you have to draw the geometry by glDrawArrays:

For instance:

float vertex_attributes[] = {
     20. / WIDTH,  20. / HEIGHT, 1., 0.,
    100. / WIDTH,  20. / HEIGHT, 0., 0.,
    100. / WIDTH, 100. / HEIGHT, 0., 1.,
     20. / WIDTH, 100. / HEIGHT, 1., 1.
};

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &vertex_attributes);
glEnableVertexAttribArray(0);
glDrawArrays(GL_QUADS, 0, 4);
glDisableVertexAttribArray(0);

Please note, that glewInit() has to be done after glfwMakeContextCurrent() but before any OpenGL instruction.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174