Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple output from fragment shader using a FBO

I have the following OpenGL/GLSL code. I'm trying to take two textures into a shader and get two different textures out.

At the moment I'm only doing pointless calculations. But for my actually application (HDR imaging) I need to get two+ textures in and out of a single shader.

My issue is in the int main() I don't know how to display one of the output textures on a quad.

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <stdio.h>

const GLchar* vertexSource =
    "#version 150 core\n"
    "in vec2 position;"
    "in vec3 color;"
    "in vec2 texcoord;"
    "out vec2 Texcoord;"
    "out vec3 Color;"
    "void main() {"
    "   Color = color;"
    "   Texcoord = texcoord;"
    "   gl_Position = vec4(position, 0.0, 1.0);"
    "}";

const GLchar* fragmentSource =
    "#version 150 core\n"
    "in vec3 Color;"
    "in vec2 Texcoord;"
    "out vec4 outColor1;"
    "out vec4 outColor2;"
    "uniform sampler2D texLena;"
    "uniform sampler2D texTex7;"
    "void main() {"
    "   vec4 colLena = texture(texLena, Texcoord);"
    "   vec4 colTex7 = texture(texTex7, Texcoord);"
    "   outColor1 = mix(colLena, colTex7, 0.75) * vec4(Color, 1.0);"
    "   outColor2 = mix(colLena, colTex7, 0.25);"
    "}";

void printShaderInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

    glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
        printf("%s\n",infoLog);
        free(infoLog);
    }
}

void printProgramInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

    glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
        printf("%s\n",infoLog);
        free(infoLog);
    }
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

int main()
{
    if (glfwInit() != GL_TRUE)
    {
        fprintf(stderr, "Failed to initialize GLFW\n");
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", glfwGetPrimaryMonitor(), NULL);

    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);

    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK)
    {
        fprintf(stderr, "Failed to initialize GLEW\n");
        return -1;
    }

     GLuint vao;
     glGenVertexArrays(1, &vao);
     glBindVertexArray(vao);

     GLuint vbo;
     glGenBuffers(1, &vbo);

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

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

     GLuint ebo;
     glGenBuffers(1, &ebo);

     GLuint elements[] = {
        0, 1, 2,
        2, 3, 0
     };

     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

     GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
     glShaderSource(vertexShader, 1, &vertexSource, NULL);
     glCompileShader(vertexShader);
     printShaderInfoLog(vertexShader);

     GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
     glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
     glCompileShader(fragmentShader);
     printShaderInfoLog(fragmentShader);

     GLuint shaderProgram = glCreateProgram();
     glAttachShader(shaderProgram, vertexShader);
     glAttachShader(shaderProgram, fragmentShader);
     glBindFragDataLocation(shaderProgram, 0, "outColor1");
     glBindFragDataLocation(shaderProgram, 1, "outColor2");
     glLinkProgram(shaderProgram);
     printProgramInfoLog(shaderProgram);
     glUseProgram(shaderProgram);

     GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
     glEnableVertexAttribArray(posAttrib);
     glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7*sizeof(float), 0);

     GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
     glEnableVertexAttribArray(colAttrib);
     glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE,
                            7*sizeof(float), (void*)(2*sizeof(float)));

     GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
     glEnableVertexAttribArray(texAttrib);
     glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE,
                            7*sizeof(float), (void*)(5*sizeof(float)));

     GLuint textures[2];
     glGenTextures(2, textures);

     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, textures[0]);
     cv::Mat image = cv::imread("lena.tiff");
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0,
                    GL_BGR, GL_UNSIGNED_BYTE, image.data);
     image.release();

     glUniform1i(glGetUniformLocation(shaderProgram, "texLena"), 0);

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

     glActiveTexture(GL_TEXTURE1);
     glBindTexture(GL_TEXTURE_2D, textures[1]);
     image = cv::imread("tex7.jpg");
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0,
                    GL_BGR, GL_UNSIGNED_BYTE, image.data);

     glUniform1i(glGetUniformLocation(shaderProgram, "texTex7"), 1);

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

     GLuint frameBuffer;
     glGenFramebuffers(1, &frameBuffer);
     glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

     GLuint texFront;
     glGenTextures(1, &texFront);
     glBindTexture(GL_TEXTURE_2D, texFront);

     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows,0, 
                    GL_RGB, GL_UNSIGNED_BYTE, NULL);

     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texFront, 0);

     GLuint texBack;
     glGenTextures(1, &texBack);
     glBindTexture(GL_TEXTURE_2D, texBack);

     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows,0, 
                    GL_RGB, GL_UNSIGNED_BYTE, NULL);

     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texBack, 0);

     image.release();

     GLenum bufs[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
     glDrawBuffers(2, bufs);

    while(!glfwWindowShouldClose(window))
    {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // What should I be doing here?
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texFront);

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteTextures(1, textures);
    glDeleteTextures(1, &texFront);
    glDeleteTextures(1, &texBack);

    glDeleteFramebuffers(1, &frameBuffer);

    glDeleteProgram(shaderProgram);
    glDeleteShader(fragmentShader);
    glDeleteShader(vertexShader);

    glDeleteBuffers(1, &ebo);
    glDeleteBuffers(1, &vbo);

    glDeleteVertexArrays(1, &vao);

    glfwTerminate();

    return 0;
}

How do I select texFront (which is my COLOR_ATTACHMENT0) to be displayed when I glDrawElements?

EDIT

enter image description here

I am getting a weird result where part of COLOR_ATTACHMENT0 appears when I try display COLOR_ATTACHMENT1

like image 922
Maggick Avatar asked Oct 25 '25 17:10

Maggick


1 Answers

To bind multiple textures for use in a shader (as you already have):

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
  • Then glUniform1i to point the uniform sampler2Ds to the texture bind points (0/1).

  • Read as normal (i.e. texture()/texelFetch() (texture2D() < GL3).

To write to multiple textures (as you already have):

  • Attach multiple textures as colour attachments with glFramebufferTexture2D and then set glDrawBuffers.

  • Bind the framebuffer.

  • Draw your geometry, writing data from the fragment shader.

    With < GL3:

    • Write to gl_FragData[*]

    Or >= GL3:

    • glBindFragDataLocation and out colour1, colour2

ARB_image_load_store is an alternative, but not necessary here.

Side note: reading from a texture bound to the current FBO is possible and works if there are no RMW hazards.

To display the output, if desired

Either:

  • Draw a full screen polygon, and use a fragment shader to draw whatever you want (i.e. a passthrough for the texture you just wrote to using the FBO). This can be done with a single really big triangle clipped using glViewport (it doesn't matter if bits go outside the screen).

    Note that to draw anything to the default framebuffer, unbind any currently bound FBO with glBindFramebuffer(GL_FRAMEBUFFER, 0);

  • Much simpler, blit the contents of your FBO to the default framebuffer.

    https://www.opengl.org/sdk/docs/man3/xhtml/glBlitFramebuffer.xml

    glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); //destination
    glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    

    The colour attachments that are copied can be set with glReadBuffer/glDrawBuffers`. See here. Another here.

like image 174
jozxyqk Avatar answered Oct 27 '25 07:10

jozxyqk