1

我正在将一个对象渲染到屏幕上。我需要在顶点着色器中为每个像素计算可见的 XYZ 坐标和法线,以便进一步计算。

是否有可能获得这些值?

我能想到的最接近的是使用屏幕外渲染(如何在 OpenGL 上渲染屏幕外?)分别渲染每个坐标(我必须渲染 6 次,效率不高)。为此,我必须将浮点数拆分为字节值。是否可以使用类似的东西

(value & 0x0000FF00) >> 8)

在顶点着色器中?

编辑:我的问题不清楚。

附加信息:我想检索每个像素的 XYZ 世界坐标和相应的法线,例如(例如 X = -0.2,Y = 0.5,Z = 1.3;NX = -0.1,NY = 0.8,NZ = 0.1)

到目前为止,我的管道与“鲍里斯”在他的回答中发布的非常相似。

4

3 回答 3

1

您要做的是渲染到所谓的Geometry Buffer这是延迟渲染中的一个常见步骤,因此我强烈建议您查看 DR 教程,了解它是如何完成的。谷歌发现了很多,但这一个出现在 opengl.org 新闻部分:http ://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html

于 2013-06-28T10:04:25.097 回答
0

如果您在顶点着色器中使用这些值设置不同的变量,那么在片段着色器中,您将获得为每个像素插值的这些值。

请注意,在 OpenGL 4.3 中,使用输入和输出变量而不是变量。

于 2013-06-28T07:29:19.110 回答
0

为了选择 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_ATTACHMENT0location=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);

你应该很高兴:-)

于 2013-06-28T07:53:55.203 回答