使用FBO从片段着色器输出多个输出

时间:2014-09-14 16:21:39

标签: c++ opengl glsl glfw glew

我有以下OpenGL / GLSL代码。我试图将两个纹理带入着色器并获得两个不同的纹理。

目前我只做无意义的计算。但对于我的实际应用(HDR成像),我需要在一个着色器中输入和输出两个+纹理。

我的问题出在int main()我不知道如何在四边形上显示其中一个输出纹理。

#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;
}

如何texFront选择要显示的COLOR_ATTACHMENT0(我的glDrawElements)?

修改

enter image description here

当我尝试显示COLOR_ATTACHMENT0

时出现COLOR_ATTACHMENT1的部分内容,我得到了一个奇怪的结果

1 个答案:

答案 0 :(得分:3)

绑定多个纹理以在着色器中使用(如您所知):

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

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
  • 然后glUniform1iuniform sampler2D指向纹理绑定点(0/1)。

  • 正常阅读(即texture()/texelFetch()texture2D()&lt; GL3)。

要写入多个纹理(如您所见):

  • 使用glFramebufferTexture2D将多个纹理作为颜色附件附加,然后设置glDrawBuffers

  • 绑定帧缓冲区。

  • 绘制几何体,从片段着色器中写入数据。

    使用&lt; GL3:

    • 写信至gl_FragData[*]

    或&gt; = GL3:

    • glBindFragDataLocationout colour1, colour2

ARB_image_load_store是另一种选择,但这里没有必要。

旁注:如果没有RMW危险,从绑定到当前FBO的纹理中读取possible and works

如果需要,显示输出

或者:

  • 绘制一个全屏多边形,并使用片段着色器绘制您想要的任何内容(即您刚刚使用FBO编写的纹理的直通)。这可以通过使用glViewport剪切的单个非常大的三角形来完成(如果位在屏幕之外,则无关紧要。)

    请注意,要将任何内容绘制到默认的帧缓冲区,请使用glBindFramebuffer(GL_FRAMEBUFFER, 0);取消绑定任何当前绑定的FBO

  • 更简单一点,将FBO的内容blit到默认的帧缓冲区。

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

    可以使用glReadBuffer / glDrawBuffers`设置复制的颜色附件。见here。另一个here