为了选择 CPU 上的信息,您确实必须使用帧缓冲区对象(FBO) 在纹理上进行渲染。您可以使用多个 将多个纹理图像附加到单个 FBO,GL_COLOR_ATTACHMENTi
然后在片段着色器中一次全部绘制。这称为多重渲染目标(MRT)。根据 datenwolf 和 Wikipedia 的说法,一个典型的用途似乎是延迟着色(我个人从未这样做过,但我做了 MRT,用于 GPU 选择)。
顶点着色器:
首先,您需要将信息从顶点着色器传输到片段着色器。
#version 330
uniform mat4 pvmMatrix;
in vec3 position;
in vec3 normal;
out vec3 fragPosition;
out vec3 fragNormal;
void main()
{
fragPosition = position;
fragNormal = normal;
gl_Position = pvmMatrix * vec4(position, 1.0);
}
片段着色器
然后,您可以通过指定不同的输出在片段着色器中渲染此信息。
#version 330
in vec3 fragPosition;
in vec3 fragNormal;
layout(location=0)out vec3 mrtPosition;
layout(location=1)out vec3 mrtNormal;
void main()
{
mrtPosition = fragPosition;
mrtNormal = fragNormal;
}
layout(location=0)
指定渲染目标将是GL_COLOR_ATTACHMENT0
(location=1
是 for GL_COLOR_ATTACHMENT1
,依此类推)。你给变量的名字并不重要。
创建 FBO
在您的 C++ 代码中,您必须设置具有多个渲染目标的 FBO。为简洁起见,我不会给出任何 FBO 的共同点,请使用第一个链接。这里重要的是:
生成多个纹理:
// The texture for the position
GLuint texPosition;
glGenTextures(1, &texPosition);
glBindTexture(GL_TEXTURE_2D, texPosition);
// [...] do the glTexParameterf(...) that suits your needs
glTexImage2D(GL_TEXTURE_2D, 0,
GL_RGB32F, // this should match your fragment shader output.
// I used vec3, hence a 3-componont 32bits float
// You can use something else for different information
viewportWidth, viewportHeight, 0,
GL_RGB, GL_FLOAT, // Again, this should match
0);
// the texture for the normal
GLuint texNormal;
glGenTextures(1, &texNormal);
// [...] same as above
请注意如何为要存储在纹理上的信息使用不同的数据类型。在这里,我vec3
在片段着色器中使用,因此我必须GL_RGB32F
用于纹理创建。在此处查看您可以使用的类型的详尽列表(例如,我使用 unsigned int 来执行 GPU 选择)。
将这些纹理附加到 FBO 的不同颜色附件:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texPosition, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
GL_TEXTURE_2D, texNormal, 0);
当然,您可能想要添加深度附件,以及您将使用 FBO 执行的所有其他“经典”操作。
绘画
下一步,您必须使用 FBO 绘制场景。
// set rendering destination to FBO
glBindFramebuffer(GL_FRAMEBUFFER, myFBO);
// Set which GL_COLOR_ATTACHMENTi are the targets of the fragment shader
GLenum buffers_to_render[] = {GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};
glDrawBuffers(2,buffers_to_render); // `2` because there are two buffers
// Draw your scene
drawScene();
此时,您要查找的所有信息都以适当的格式(三分量 32 位浮点数,无需像您的问题中那样转换为字节值)以纹理的形式存储在 GPU 上。
检索 CPU 上的数据
glRead...
最后,您可以使用这些方法将纹理中的数据从 GPU 取回给 CPU 。
float * positions = new float[3*width*height];
float * normals = new float[3*width*height];
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind the FBO for writing (and reading)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_); // bind the FBO for reading
glReadBuffer(GL_COLOR_ATTACHMENT0); // set which attachment to read from
glReadPixels(0, 0, width, height, // read the pixels
GL_RGB, GL_FLOAT, // use the right format here also
positions);
glReadBuffer(GL_COLOR_ATTACHMENT1); // repeat for the normals
glReadPixels(0, 0, width, height,
GL_RGB, GL_FLOAT,
normals);
你应该很高兴:-)