【发布时间】:2016-07-25 15:00:06
【问题描述】:
对于一个应用程序,我需要创建一个网格的深度图像。基本上我想要一个图像,其中每个像素都告诉相机中心和交点之间的距离。我选择使用 OpenGL 来完成这项任务,这样大部分计算都可以在 GPU 而不是 CPU 上完成。
这是我的顶点着色器代码。它计算交点的真实坐标并将坐标存储在varying vec4 中,以便我可以在片段着色器中访问它。
varying vec4 verpos;
void main(void)
{
gl_Position = ftransform();
verpos = gl_ModelViewMatrix*gl_Vertex;
}
这里是片段着色器的代码。在片段着色器中,我使用这些坐标来使用毕达哥拉斯计算到原点 (0,0,0) 的欧式距离。为了访问 CPU 上的计算值,我的计划是使用 gl_FragColor 将距离存储为颜色,并使用 glReadPixels(0, 0, width, height, GL_RED, GL_FLOAT, &distances[0]); 提取颜色值
varying vec4 verpos;
void main()
{
float x = verpos.x;
float y = verpos.y;
float z = verpos.z;
float depth = sqrt(x*x + y*y + z*z) / 5000.0;
gl_FragColor = vec4(depth, depth, depth, 1.0);
}
问题是,我只收到0.003921569 (=1/255) 精度的结果。
结果值包含例如0.364705890, 0.364705890, 0.364705890, 0.364705890, 0.368627459, 0.368627459, 0.368627459。一行中的大部分值是完全一样的,然后有1/255的跳跃。
所以介于gl_FragColor 和glReadPixels 之间的某个地方,OpenGL 将浮点数转换为 8 位,然后将它们转换回浮点数。
如何在不丢失精度的情况下将距离值提取为浮点数?
【问题讨论】:
-
您正在写入 8 位 RGBA 的后缓冲区,如果您想写入浮点值,您需要渲染到带有
HALF_FLOAT或FLOAT纹理的FrameBufferObject附加到它,然后从该纹理中读取。 -
为什么深度是 1/5000?为什么不使用现成的
length内置函数,即length(verpos)。从技术上讲,您甚至可以省去将深度写入片段颜色的步骤(并一起禁用颜色写入),只需读取深度缓冲区的内容并线性化其中深度值的 1/x 映射。深度缓冲区至少有 16 位,更可能是 24 位。从技术上讲,您甚至可以将线性深度写入深度缓冲区,但这会影响性能。 -
@datenwolf 我除以 5000,所以值在
[0, 1]的范围内。 -
而且我不使用深度缓冲区,因为您收到的不是中心的深度,而是查看平面的深度。
-
@user38034:在 OpenGL 中,没有“中心”之类的东西,只有“一些”空间 OpenGL 对此一无所知,并且有剪辑和 NDC 空间,这是 OpenGL 操作的空间。而且顶点着色器从这个“一些”空间映射到剪辑空间(并从那里进行一些硬连线转换到 NDC 空间)。深度缓冲区在 NDC 空间中运行,仅此而已。值得一提的是,到查看平面的距离与到原点的距离没有实际差异;它归结为一个恒定的偏移量。
标签: c++ opengl glsl shader fragment-shader