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

时间:2013-06-28 07:20:07

标签: c++ opengl shader

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

到目前为止,我的管道非常类似于“鲍里斯”在答案中发布的内容。

3 个答案:

答案 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_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来创建纹理。检查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);

你应该好好去: - )