我有一个计算着色器生成纹理的示例,然后片段着色器呈现到占据整个窗口的四边形。
在片段着色器代码中,我看到一个统一的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;
}
答案 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()
次调用。