这个纹理如何传递给片段着色器?
问题描述:
我有一个计算着色器的例子,生成一个片段着色器然后渲染到一个占用了整个窗口的四边形的纹理。这个纹理如何传递给片段着色器?
在片段着色器代码中,我看到了统一的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。从GLSL 4.5规范,4.3.5节:
所有统一变量是只读并且在链接时或通过API在外部初始化。链接时初始值是变量初始值设定项的值(如果存在),如果没有初始值设定项,则为0。
您需要了解下一部分是采样变量的值是你想从采样纹理单元。非常类似地,图像变量的值是用于图像访问的图像单元。
将这两块组合在一起,因为您没有为这些统一变量设置值,片段着色器中的采样器将访问绑定到纹理单元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()
调用需要进行,而相应的程序被激活。
该代码实际上是否按预期行事?我希望有一个'glGetUniformLocation()'&'glUniform()'pair *在那里,但我没有看到它。 – genpfault
是的..这段代码是从OpenGL的“红色书”中修改的。我真的很讨厌这本书。他们在一些地方使用宏和辅助函数,为了清晰起见,这是一个非常糟糕的主意,他们的解释非常糟糕。看到第12章的例子:https://github.com/openglredbook/examples/tree/master/src – Maxthecat