从clipspace.xyz 和(inv)投影矩阵计算clipspace.w
Posted
技术标签:
【中文标题】从clipspace.xyz 和(inv)投影矩阵计算clipspace.w【英文标题】:Calculate clipspace.w from clipspace.xyz and (inv) projection matrix 【发布时间】:2013-01-09 12:31:47 【问题描述】:我正在使用对数深度算法,这会导致 someFunc(clipspace.z) 被写入深度缓冲区并且没有隐式透视除法。
我正在做 RTT / 后处理,所以稍后在片段着色器中我想重新计算 eyespace.xyz,给定 ndc.xy(来自片段坐标)和 clipspace.z(来自 someFuncInv() 中存储的值深度缓冲区)。
请注意,我没有 clipspace.w,并且我存储的值不是 clipspace.z / clipspace.w(就像使用固定函数深度时一样) - 所以类似于 ...
float clip_z = ...; /* [-1 .. +1] */
vec2 ndc = vec2(FragCoord.xy / viewport * 2.0 - 1.0);
vec4 clipspace = InvProjMatrix * vec4(ndc, clip_z, 1.0));
clipspace /= clipspace.w;
...在这里不起作用。
那么,给定投影矩阵或它的逆矩阵,有没有办法从clipspace.xyz 中计算clipspace.w?
【问题讨论】:
我们不会在我们的问题中提供答案。如果您想与他人分享您的解决方案代码,您可以写下您的问题的答案并将其放在那里。 【参考方案1】:clipspace.xy = FragCoord.xy / viewport * 2.0 - 1.0;
这在命名方面是错误的。 “剪辑空间”是顶点着色器(或任何最后一个顶点处理阶段)输出的空间。剪辑空间和窗口空间之间是标准化设备坐标 (NDC) 空间。 NDC空间是裁剪空间除以裁剪空间W坐标:
vec3 ndcspace = clipspace.xyz / clipspace.w;
所以第一步就是取我们的窗口空间坐标,得到NDC空间坐标。这很容易:
vec3 ndcspace = vec3(FragCoord.xy / viewport * 2.0 - 1.0, depth);
现在,我将假设您的 depth
值是正确的 NDC 空间深度。我假设您从深度纹理中获取值,然后使用它渲染的深度范围近/远值将其映射到 [-1, 1] 范围。如果你没有,你应该这样做。
那么,既然我们有了ndcspace
,我们如何计算clipspace
?嗯,这很明显:
vec4 clipspace = vec4(ndcspace * clipspace.w, clipspace.w);
很明显而且...没有帮助,因为我们没有clipspace.w
。那么我们如何获得呢?
要得到这个,我们需要看看clipspace
第一次是如何计算的:
vec4 clipspace = Proj * cameraspace;
这意味着clipspace.w
是通过获取cameraspace
并通过Proj
的第四行进行点积计算得出的。
嗯,这不是很有帮助。如果我们实际查看Proj
的第四行,它会更有帮助。当然,您可以使用任何投影矩阵,如果您不使用典型的投影矩阵,这种计算会变得更加困难(可能是不可能的)。
Proj
的第四行,使用典型的投影矩阵,真的就是这样:
[0, 0, -1, 0]
这意味着clipspace.w
实际上只是-cameraspace.z
。这对我们有什么帮助?
记住这一点会有所帮助:
ndcspace.z = clipspace.z / clipspace.w;
ndcspace.z = clipspace.z / -cameraspace.z;
嗯,这很好,但它只是将一个未知数换成了另一个未知数;我们仍然有一个包含两个未知数的方程(clipspace.z
和 cameraspace.z
)。但是,我们确实知道其他事情:clipspace.z
来自于对我们投影矩阵的 第三 行进行点积 cameraspace
。传统投影矩阵的第三行是这样的:
[0, 0, T1, T2]
其中 T1 和 T2 是非零数字。我们暂时忽略这些数字。因此,clipspace.z
实际上只是 T1 * cameraspace.z + T2 * cameraspace.w
。如果我们知道cameraspace.w
是 1.0(通常是这样),那么我们可以删除它:
ndcspace.z = (T1 * cameraspace.z + T2) / -cameraspace.z;
所以,我们仍然有一个问题。事实上,我们没有。为什么?因为在这个euqation中只有一个未知数。请记住:我们已经知道ndcspace.z
。因此我们可以使用 ndcspace.z 来计算cameraspace.z
:
ndcspace.z = -T1 + (-T2 / cameraspace.z);
ndcspace.z + T1 = -T2 / cameraspace.z;
cameraspace.z = -T2 / (ndcspace.z + T1);
T1
和 T2
直接来自我们的投影矩阵(场景最初渲染的那个)。我们已经有了ndcspace.z
。所以我们可以计算cameraspace.z
。我们知道:
clispace.w = -cameraspace.z;
因此,我们可以这样做:
vec4 clipspace = vec4(ndcspace * clipspace.w, clipspace.w);
显然你需要一个浮点数来表示clipspace.w
而不是文字代码,但你明白我的意思。一旦你有了clipspace
,为了获得相机空间,你乘以逆投影矩阵:
vec4 cameraspace = InvProj * clipspace;
【讨论】:
非常感谢您的详细解释(我根据您的评论修正了我的变量名称)。你的帖子完全反映了我的思路——我强烈认为投影矩阵是“正常的”允许重构 w(只要原始向量是齐次的)——但这只是直觉,我应该写下点积意识到这一点......我还没有真正尝试过(我刚刚看到你的回复)但它非常有意义,所以我认为我的问题已经解决了。再次感谢。 :-) 记录在案 - 从我昨天问我的问题到今天在这里找到答案,有人写了这个页面上的第二部分 opengl.org/wiki/Compute_eye_space_from_window_space 处理这个话题......这不完全是我的问题,但数学非常接近。以上是关于从clipspace.xyz 和(inv)投影矩阵计算clipspace.w的主要内容,如果未能解决你的问题,请参考以下文章
如何从 WinRT Windows.Media API 中获取 Hololens 可定位相机视图和投影矩阵