[英]How is this texture passed to the fragment shader?
我有一个计算着色器生成纹理的示例,然后片段着色器将其渲染到占据整个窗口的四边形。
在片段着色器代码中,我看到了一个统一的 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()
设置活动纹理单元, 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()
调用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.