2

I want to retrieve the color of the current texel in my fragment shader so I can adapt the output color depending on it.

For example: if the current texel's color is red then set the color to green. enter image description here Above, you can see there are two triangles. One triangle is partially drawn on the other. You can see the superimposed portion is in green as, like I said, my fragment shader detected the color "red" so it set the output color to "green".

As you can see, my program works well. However, I'm a very beginner in OpenGL so I don't quite understand everything I wrote and I'm sure I did a few mistakes. I'd like your feedback.

What I did to achieve this:

  1. Draw first triangle in a FBO
  2. Copy the framebuffer to the draw framebuffer (= on screen)
  3. Draw the second triangle directly on screen

At first I tried to draw the two triangles directly on screen without a FBO but then I couldn't get the texel color with texture2D() in my fragment shader.

Here is my code (I use OpenGL 3.3, GLFW 3 and GLEW):

#include <iostream>
#include <string>

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>

const std::string WINDOW_TITLE = "Drawing color depending on current background pixel color";
const GLuint WIDTH = 800, HEIGHT = 600;

const std::string vertexShaderSource = R"(
#version 330 core

layout (location = 0) in vec3 position;

void main()
{
    gl_Position = vec4(position.x, position.y, position.z, 1.0f);
}
)";

const std::string fragmentShaderSource = R"(
#version 330 core

layout(location = 0) out vec4 color;
uniform sampler2D renderedTexture;

void main()
{
    vec2 size = textureSize(renderedTexture, 0);
    vec2 position = gl_FragCoord.xy / size.xy;
    vec4 inputColor = texture2D(renderedTexture, position);

    if (inputColor[0] == 0.0f)
    {
        color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
    }
    else if (inputColor[0] == 1.0f)
    {
        color = vec4(0.1f, 1.0f, 0.0f, 1.0f);
    }
    else
    {
        color = vec4(0.8f, 0.0f, 1.0f, 1.0f);
    }
}
)";


GLFWwindow *setupWindow()
{
    GLFWwindow *window = nullptr;
    int width = 0, height = 0;

    glfwInit();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    window = glfwCreateWindow(WIDTH, HEIGHT, WINDOW_TITLE.c_str(), nullptr, nullptr);
    glfwMakeContextCurrent(window);

    glewExperimental = GL_TRUE;
    glewInit();

    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    return window;
}

GLuint compileShader(GLenum type, const std::string &source, std::string &errorLog)
{
    GLuint shader = 0;
    GLint compileSuccess = 0;
    const GLchar *sourceC = source.c_str();
    const std::size_t ERROR_LOG_BUFFER_SIZE = 512;
    GLchar errorLogBuffer[ERROR_LOG_BUFFER_SIZE]{};

    errorLog = "";

    shader = glCreateShader(type);
    glShaderSource(shader, 1, &sourceC, nullptr);
    glCompileShader(shader);

    glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
    if (!compileSuccess)
    {
        glGetShaderInfoLog(shader, ERROR_LOG_BUFFER_SIZE, nullptr, errorLogBuffer);
        glDeleteShader(shader);

        errorLog = errorLogBuffer;

        return 0;
    }

    return shader;
}

GLuint compileShaderProgram(const std::string &vertexShaderSource, const std::string &fragmentShaderSource, std::string &errorLog)
{
    GLuint vertexShader{};
    GLuint fragmentShader{};
    GLuint shaderProgram{};
    GLint linkSuccess{};
    const std::size_t ERROR_LOG_BUFFER_SIZE = 512;
    GLchar errorLogBuffer[ERROR_LOG_BUFFER_SIZE]{};

    errorLog = "";

    vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource, errorLog);
    if (vertexShader == 0)
        return 0;

    fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource, errorLog);
    if (fragmentShader == 0)
        return 0;

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkSuccess);
    if (!linkSuccess)
    {
        glGetProgramInfoLog(shaderProgram, ERROR_LOG_BUFFER_SIZE, nullptr, errorLogBuffer);
        glDeleteProgram(shaderProgram);

        errorLog = errorLogBuffer;

        return 0;
    }

    return shaderProgram;
}

int main(int argc, char *argv[])
{
    GLFWwindow *window = setupWindow();
    std::string errorLog = "";
    GLuint shaderProgram = compileShaderProgram(vertexShaderSource, fragmentShaderSource, errorLog);
    GLuint fbo = 0;
    GLuint fboTexture = 0;
    GLuint vao = 0;
    GLuint vbo = 0;
    GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
    GLuint indicesBuffer = 0;
    GLushort indices[] = { 0,1,2, 3,4,5 };
    GLfloat vertices[] = {
        // Bottom triangle (first triangle drawn)
        -0.5f, +0.0f,
        +0.5f, +0.0f,
        +0.0f, +0.5f,

        // Top triangle (second triangle drawn, partially ON the first one, and slighly above it)
        -0.5f, +0.2f,
        +0.5f, +0.2f,
        +0.0f, +0.8f,
    };


    /*
    ** Setup FBO
    */
    glEnable(GL_TEXTURE_2D);

    glGenFramebuffers(1, &fbo);
    glGenTextures(1, &fboTexture);

    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glBindTexture(GL_TEXTURE_2D, fboTexture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTexture, 0);
    glDrawBuffers(sizeof(drawBuffers) / sizeof(GLenum), drawBuffers);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        return EXIT_FAILURE;


    /*
    ** Setup VAO and VBO
    */
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &indicesBuffer);

    glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


    /*
    ** Main loop
    */
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();

        /*
        ** Draw first triangle in FBO (not on screen)
        */
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
        glUseProgram(0);


        /*
        ** Copy the read framebuffer to the draw framebuffer (= on screen)
        */
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glViewport(0, 0, WIDTH, HEIGHT);
        glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);


        /*
        ** Draw the second triangle directly on screen
        */
        glUseProgram(shaderProgram);
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast<const void*>(3 * sizeof(GLushort)));
        glUseProgram(0);


        /*
        ** Swap buffers
        */
        glfwSwapBuffers(window);
    }


    /**
    ** Free resources
    */
    glDeleteBuffers(1, &indicesBuffer);
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(shaderProgram);

    glfwTerminate();


    return 0;
}
  • Is it a good way of doing it?
  • Is it called "multisampling"? From what I understand, you use FBO(s) with functions like glTexImage2DMultisample() to do multisampling. But even if I use an FBO, I use glTexImage2D() instead of glTexImage2DMultisample(). So?

Also, I don't quite understand how the uniform sampler2D renderedTexture; in my fragment shader works. I thought I needed to do something like:

GLuint texID = glGetUniformLocation(shaderProgram, "renderedTexture");
glUniform1i(texID, 0);

to set the uniform variable, but I don't do it and it works. Why?

Finally, you might wonder why I want to do all this. Well, as I just started learning OpenGL, I do that as an "exercise" to solve my real problem: write text on an image in a color with enough contrast so it can be readable (e.g.: if you want to write some text on a white wall, write it in black, not in white, or you won't see it).

GuiTeK
  • 1,561
  • 5
  • 20
  • 39

0 Answers0