在opengl中从模型中获取可见的XYZ坐标和法线

Acquire visible XYZ-coordinates and normals from model in opengl

本文关键字:坐标 XYZ opengl 模型 获取      更新时间:2023-10-16

我正在屏幕上呈现一个对象。我需要在顶点着色器中为每个像素计算的可见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)

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

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

为了在CPU上选择信息,您确实必须使用帧缓冲区对象(FBO)在纹理上渲染。通过使用多个GL_COLOR_ATTACHMENTi,可以将多个纹理图像附加到单个FBO,并在片段着色器中同时绘制。这被称为多重渲染目标(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_ATTACHMENT0location=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来创建纹理。在这里查看您可以使用的类型的详尽列表(例如,我使用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();

在这一点上,您正在寻找的所有信息都以纹理的形式存储在GPU上,并以适当的格式存储(三个32位浮动组件,无需像您的问题中那样转换为字节值)。

检索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);

你应该很好去:-)

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

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