我正在将对象渲染到屏幕上。我需要在顶点着色器中为每个像素计算的可见XYZ坐标和法线,以便进一步计算。
是否有可能获得这些价值?
我能想到的最接近的是使用屏幕外渲染(How to render offscreen on OpenGL?)来分别渲染每个坐标(我必须渲染6次,效率不高)。为此,我必须将浮点数拆分为字节值。是否可以使用
之类的东西(value & 0x0000FF00) >> 8)
在顶点着色器中?
编辑:我的问题不明确。
附加信息:我想检索每个像素的XYZ世界坐标和相应的法线,例如: (例如X = -0.2,Y = 0.5,Z = 1.3; NX = -0.1,NY = 0.8,NZ = 0.1)
到目前为止,我的管道非常类似于“鲍里斯”在答案中发布的内容。
答案 0 :(得分:1)
您要做的是渲染到所谓的几何缓冲区。这是 Deferred Rendering 中的常见步骤,因此我强烈建议您查看DR教程,了解它是如何完成的。谷歌找到了很多,但这个在opengl.org新闻栏目中有特色:http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html
答案 1 :(得分:0)
如果在顶点着色器中使用这些值设置变量变量,则在片段着色器中,您将获得为每个像素插值的值。
请注意,在OpenGL 4.3中使用输入和输出变量而不是变化。
答案 2 :(得分:0)
为了获取CPU上的信息,您确实必须使用Frame Buffer Object(FBO)渲染纹理。您可以使用多个GL_COLOR_ATTACHMENTi
将多个纹理图像附加到单个FBO,并在片段着色器中一次性绘制。这称为Multiple Render Target(MRT)。根据datenwolf和维基百科的说法,一个典型的用法似乎是延迟着色(我从未亲自做过,但我选择了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
用于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
来创建纹理。检查here以获取可以使用的详尽类型列表(例如,我使用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格式。 / p>
在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);
你应该好好去: - )