这个纹理是如何传递给片段着色器的?

时间:2016-09-28 01:01:00

标签: c++ opengl

我有一个计算着色器生成纹理的示例,然后片段着色器呈现到占据整个窗口的四边形。

在片段着色器代码中,我看到一个统一的sampler2D,但是计算着色器的输出实际上是如何传递给片段着色器的?它只是因为受到束缚?更好的做法是将纹理(通过统一或其他方法)显式绑定到片段/顶点着色器?

// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>

// Include GLEW
#include <GL/glew.h>

//Glut
#include <GL/glut.h>

const GLchar* computeSource =
    "#version 430 core\n"
    "\n"
    "layout (local_size_x = 32, local_size_y = 16) in;\n"
    "\n"
    "layout (rgba32f) uniform image2D output_image;\n"
    "void main(void)\n"
    "{\n"
    "    imageStore(output_image,\n"
    "    ivec2(gl_GlobalInvocationID.xy),\n"
    "    vec4(vec2(gl_LocalInvocationID.xy) / vec2(gl_WorkGroupSize.xy), 0.0, 0.0));\n"
    "}\n";

const GLchar* vertexSource =
        "#version 430 core\n"
        "\n"
        "in vec4 vert;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "    gl_Position = vert;\n"
        "}\n";

const GLchar* fragmentSource =
        "#version 430 core\n"
        "\n"
        "layout (location = 0) out vec4 color;\n"
        "\n"
        "uniform sampler2D output_image;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "    color = texture(output_image, vec2(gl_FragCoord.xy) / vec2(textureSize(output_image, 0)));\n"
        "}\n";

GLuint vao;
GLuint vbo;
GLuint mytexture;
GLuint shaderProgram;
GLuint computeProgram;

void checkError(int line)
{
    GLint err;

    do
    {
        err = glGetError();
        switch (err)
        {
            case GL_NO_ERROR:
                //printf("%d: No error\n", line);
                break;
            case GL_INVALID_ENUM:
                printf("%d: Invalid enum!\n", line);
                break;
            case GL_INVALID_VALUE:
                printf("%d: Invalid value\n", line);
                break;
            case GL_INVALID_OPERATION:
                printf("%d: Invalid operation\n", line);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                printf("%d: Invalid framebuffer operation\n", line);
                break;
            case GL_OUT_OF_MEMORY:
                printf("%d: Out of memory\n", line);
                break;
            default:
                printf("%d: glGetError default case. Should not happen!\n", line);
        }
    } while (err != GL_NO_ERROR);
}

void display()
{

    glUseProgram(computeProgram);
    glBindImageTexture(0, mytexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
    glDispatchCompute(8, 16, 1);

    glBindTexture(GL_TEXTURE_2D, mytexture);

    glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUseProgram(shaderProgram);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    glFlush();
    glutSwapBuffers();

}    


void reshape(int width,int height)
{
    double w2h = (height>0) ? (double)width/height : 1;
    //  Set viewport as entire window
    glViewport(0,0, width,height);
}



int main(int argc, char** argv)
{

    // Window Setup

    glutInitWindowSize(640, 400);
    glutInitWindowPosition (140, 140);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInit(&argc, argv);

    glutCreateWindow( "OpenGL Application" );
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);

    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        return -1;
    }

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);

    glGenBuffers(1, &vbo);

    GLfloat vertices[] = {
        // X    Y      Z     A
        -1.0f, -1.0f, 0.5f, 1.0f,
         1.0f, -1.0f, 0.5f, 1.0f,
         1.0f,  1.0f, 0.5f, 1.0f,
        -1.0f,  1.0f, 0.5f, 1.0f,
    };

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);


    checkError(__LINE__);


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

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


    GLuint computeShader;
    computeProgram = glCreateProgram();
    computeShader = glCreateShader(GL_COMPUTE_SHADER);
    glShaderSource(computeShader, 1, &computeSource, NULL);
    glCompileShader(computeShader);
    glAttachShader(computeProgram, computeShader);
    glLinkProgram(computeProgram);

    glGenTextures(1, &mytexture);
    glBindTexture(GL_TEXTURE_2D, mytexture);
    glTexStorage2D(GL_TEXTURE_2D, 8, GL_RGBA32F, 256, 256);
    checkError(__LINE__);

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "color");
    glLinkProgram(shaderProgram);
    checkError(__LINE__);

    glutMainLoop();

    return 0;
}

1 个答案:

答案 0 :(得分:1)

这个工作的主要原因是着色器中的统一变量的默认值为0.从GLSL 4.5规范,4.3.5节:

  

所有统一变量都是只读的,并在链接时或通过API在外部进行初始化。链接时间初始值是变量初始化程序的值(如果存在),如果没有初始化程序则为0。

您需要了解的下一部分是,sampler变量的值是您要从中采样的纹理单元。非常相似,图像变量的值是用于图像访问的图像单元

将这两个部分放在一起,因为您没有为这些统一变量设置值,片段着色器中的采样器将访问绑定到纹理单元0的纹理。计算着色器中的图像将访问图像绑定图像单元0。

幸运的是,这正是您所需要的:

  • 由于您从未使用glActiveTexture()设置活动纹理单元,因此调用:

    glBindTexture(GL_TEXTURE_2D, mytexture);
    

    将纹理绑定到纹理单元0,这意味着它将在片段着色器中进行采样。

  • 在您绑定图片的通话中:

    glBindImageTexture(0, mytexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
    

    您传递0作为第一个参数,它指定要绑定到的图像单元。因此,计算着色器将访问此图像。

恕我直言,总是设置统一变量的值是好的风格,即使默认值足够。这使得代码更具可读性,并且一旦使用多个纹理/图像,设置统一值将是必不可少的。所以为了清楚起见,我会在你的代码中有这样的东西:

GLint imgLoc = glGetUniformLocation(computeProgram, "output_image");
glUniform1i(imgLoc, 0);
...
GLint texLoc = glGetUniformLocation(shaderProgram, "output_image");
glUniform1i(texLoc, 0);

请注意,在相应的程序处于活动状态时,需要进行glUniform1i()次调用。