我试图使用着色器来修改绑定到帧缓冲区的纹理,但是我对着色器如何获得"原始"而感到困惑。输入值。
我正在做以下事情:
GLuint textureId = 0;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, ...);
GLuint framebufferId = 0;
glGenFramebuffers(1, &framebufferId);
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
glBindTexture(GL_TEXTURE_2D, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) { ... }
glUseProgram(programId);
const GLenum buffer = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, &buffer);
空顶点和片段着色器看起来像什么?由于我没有绘制灵长类动物,如何在顶点着色器中设置gl_Position
?如何通过输入颜色作为片段着色器的输出颜色?
空顶点着色器:
#version 330
void main()
{
gl_Position = ??;
}
空片段着色器:
#version 330
layout(location = 0) out vec4 out_colour;
void main()
{
out_colour = ???;
}
答案 0 :(得分:16)
我的印象是你可以渲染到屏幕外 framebuffer,附加纹理,然后使用着色器修改 纹理,然后使用glReadPixels来获取修改后的数据。这是 我正在努力做什么。
好的,所以你想通过片段着色器提供纹理以获得新的纹理。首先,你必须记住,你不能只是就地修改纹理,因为你无法从你当前渲染的纹理中读取。您必须将要修改的纹理作为普通纹理输入到片段着色器中,并像往常一样将结果输出到帧缓冲区中,这可能是附加不同纹理的FBO,渲染缓冲区(如果你想将它读回CPU,无论如何),或默认的帧缓冲。如果您只想将一个图像转换为另一个图像,只需要将结果写入屏幕外缓冲区或纹理,就不需要FBO。
此外,您仍然需要绘制一些东西,以便光栅化器生成实际片段以调用片段着色器。通常的做法是只绘制一个与观察平面平行的屏幕大小的四边形,以便用片段填充整个视口:
//initialization code
glGenVertexArrays(1, &quad_vao);
glBindVertexArray(quad_vao);
const GLfloat vertices[] = {
-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f };
glGenBuffers(1, &quad_vbo);
glBindBuffer(GL_ARRAY_BUFFER, quad_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glDeleteBuffers(1, &quad_vbo);
...
//render code
glBindVertexArray(quad_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
作为顶点着色器,一个简单的pass-thru着色器就足够了,因为顶点位置已经在剪辑空间中:
#version 330
layout(location = 0) in vec4 in_position;
void main()
{
gl_Position = in_position;
}
在片段着色器中,我们将纹理作为输入。纹理坐标已经由片段在屏幕上的位置给出,我们只需要通过划分纹理大小来标准化(或者使用GL_TEXTURE_RECTANGLE
和相应的samplerRect
来直接使用片段坐标):
#version 330
uniform sampler2D tex;
uniform vec2 tex_size;
layout(location = 0) out vec4 out_color;
void main()
{
vec4 in_color = texture(tex, gl_FragCoord.xy / tex_size);
out_color = //do whatever you want with in_color;
}
就是这样,修改过的纹理被写入帧缓冲区,无论重定向的位置或者之后使用帧缓冲区数据做什么。
编辑使用OpenGL 4.3及其计算着色器,现在有一种更直接的方法可用于非光栅化纯GPGPU任务,如图像处理。您可以在常规2D域和进程上调用计算着色器(与其他GPU计算框架更相似,如 CUDA 或 OpenCL ,而不是其他OpenGL着色器)一个纹理(使用OpenGL 4.2的图像加载/存储功能)直接就地。在这种情况下,您只需要相应的计算着色器:
#version 430
layout(local_size_x=32,local_size_y=8) in; //or whatever fits hardware and shader
layout(binding = 0, rgba) uniform image2D img; //adjust format to the actual data
void main()
{
const uint2 idx = gl_GlobalInvocationID.xy;
vec4 color = imageLoad(img, idx);
//do whatever you want with color
imageStore(img, idx, color);
}
然后你需要做的就是将纹理绑定到相应的图像单元(0,在着色器中设置)并在二维图像域上调用计算着色器:
//again use the format that fits the texture data
glBindImageTexture(0, textureId, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
glUseProgram(compute_program); //a program with a single GL_COMPUTE_SHADER
glDispatchCompute(texture_width, texture_height, 1);
就是这样,你不需要一个FBO,你不需要任何其他着色器,你不需要绘制任何东西,只需要原始计算。但是,如果这种更直接的方法也能带来更好的性能,则必须对其进行评估。同样,您可能需要注意要修改纹理的正确内存同步,尤其是在尝试从之后读取它时。但请参阅有关图像加载/存储的更深入的资料以获取更多信息。