【发布时间】:2011-08-05 20:56:12
【问题描述】:
我如何从窗口空间(帧缓冲区中的像素)坐标 + GLSL 中的像素深度值计算眼睛空间坐标(可以说是 GLSL 中的 gluUnproject)?
【问题讨论】:
我如何从窗口空间(帧缓冲区中的像素)坐标 + GLSL 中的像素深度值计算眼睛空间坐标(可以说是 GLSL 中的 gluUnproject)?
【问题讨论】:
看起来与 GLSL convert gl_FragCoord.z into eye-space z 重复。
编辑(完整答案):
// input: x_coord, y_coord, samplerDepth
vec2 xy = vec2(x_coord,y_coord); //in [0,1] range
vec4 v_screen = vec4(xy, texture(samplerDepth,xy), 1.0 );
vec4 v_homo = inverse(gl_ProjectionMatrix) * 2.0*(v_screen-vec4(0.5));
vec3 v_eye = v_homo.xyz / v_homo.w; //transfer from homogeneous coordinates
【讨论】:
假设您坚持使用固定的管道样式模型、视图和投影,您可以完全实现 gluUnProject man page 中给出的公式。
GLSL 中没有内置矩阵求逆,因此理想情况下,您应该在 CPU 上这样做。因此,您需要提供一个与您的组合 modelViewProjection 矩阵相反的统一。 gl_FragCoord 位于窗口坐标中,因此您还需要提供视图尺寸。
所以,你最终可能会得到类似(即席编码):
vec4 unProjectedPosition = invertedModelViewProjection * vec4(
2.0 * (gl_FragCoord.x - view[0]) / view[2] - 1.0,
2.0 * (gl_FragCoord.y - view[1]) / view[3] - 1.0,
2.0 * gl_FragCoord.z - 1.0,
1.0);
如果您已经实现了自己的旧矩阵堆栈的模拟,那么您可能很好地反转矩阵。否则,more daunting topic 可能比您预期的要好,您最好使用 MESA 的 open source implementation(请参阅该文件中的第三个函数 invert_matrix),因为它已经过良好测试。
【讨论】:
好吧,opengl.org 上的一个人指出,投影产生的剪辑空间坐标除以 clipPos.w 以计算归一化的设备坐标。当从片段到 ndc 到剪辑空间坐标的步骤反转时,您需要重建 w(恰好是来自相应视图空间(相机)坐标的 -z),并将 ndc 坐标乘以该值以计算正确的剪辑空间坐标(您可以通过将其与逆投影矩阵相乘来将其转换为视图空间坐标)。
以下代码假定您在后期处理中处理帧缓冲区。在渲染几何体时处理它时,可以使用 gl_FragCoord.z 代替 texture2D (sceneDepth, ndcPos.xy).r。
代码如下:
uniform sampler2D sceneDepth;
uniform mat4 projectionInverse;
uniform vec2 clipPlanes; // zNear, zFar
uniform vec2 windowSize; // window width, height
#define ZNEAR clipPlanes.x
#define ZFAR clipPlanes.y
#define A (ZNEAR + ZFAR)
#define B (ZNEAR - ZFAR)
#define C (2.0 * ZNEAR * ZFAR)
#define D (ndcPos.z * B)
#define ZEYE -(C / (A + D))
void main()
{
vec3 ndcPos;
ndcPos.xy = gl_FragCoord.xy / windowSize;
ndcPos.z = texture2D (sceneDepth, ndcPos.xy).r; // or gl_FragCoord.z
ndcPos -= 0.5;
ndcPos *= 2.0;
vec4 clipPos;
clipPos.w = -ZEYE;
clipPos.xyz = ndcPos * clipPos.w;
vec4 eyePos = projectionInverse * clipPos;
}
基本上这是一个 GLSL 版本的 gluUnproject。
【讨论】:
我刚刚意识到没有必要在片段着色器中进行这些计算。您可以通过在 CPU 上执行此操作并将其与 MVP 逆相乘来节省一些操作(假设 glDepthRange(0, 1),请随意编辑):
glm::vec4 vp(left, right, width, height);
glm::mat4 viewportMat = glm::translate(
vec3(-2.0 * vp.x / vp.z - 1.0, -2.0 * vp.y / vp.w - 1.0, -1.0))
* glm::scale(glm::vec3(2.0 / vp.z, 2.0 / vp.w, 2.0));
glm::mat4 mvpInv = inverse(mvp);
glm::mat4 vmvpInv = mvpInv * viewportMat;
shader->uniform("vmvpInv", vmvpInv);
在着色器中:
vec4 eyePos = vmvpInv * vec4(gl_FragCoord.xyz, 1);
vec3 pos = eyePos.xyz / eyePos.w;
【讨论】:
我认为所有可用的答案都从一个方面触及问题,khronos.org 有一个 Wiki 页面,其中列出了一些不同的案例并用着色器代码进行了解释,因此值得在这里发布。
Compute eye space from window space.
【讨论】: