Opengl 根据clipspace.xyz和(inv)投影矩阵计算clipspace.w

Opengl 根据clipspace.xyz和(inv)投影矩阵计算clipspace.w,opengl,glsl,shader,direct3d,Opengl,Glsl,Shader,Direct3d,我使用的是一个对数深度算法,它会导致someFunc(clipspace.z)被写入深度缓冲区,并且没有隐式的透视除法 我正在进行RTT/后处理,因此稍后在片段着色器中,我要重新计算eyespace.xyz,给定ndc.xy(来自片段坐标)和clipspace.z(来自深度缓冲区中存储的值的someFuncInv()) 请注意,我没有clipspace.w,并且我存储的值不是clipspace.z/clipspace.w(当使用固定函数深度时会是这样),因此一些类似于 float clip_z

我使用的是一个对数深度算法,它会导致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

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);
现在,我假设您的
深度
值是正确的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;
所以,我们仍然有一个问题。事实上,我们没有。为什么?因为在这一过程中只有一个未知。记住:我们已经知道了
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被重建(只要原始向量是齐次的)-但这只是直觉,我应该写下点积来实现它。。。我还没有真正尝试过(我刚刚看到你的回复),但它是完全有意义的,所以我认为我的问题解决了。再次感谢。:-)记录在案——从我昨天提出问题到今天在这里找到答案,有人在这一页上写了第二节,讨论这个话题。。。这不完全是我的问题,但数学很接近。我们不把答案放在问题里。如果您想与他人共享您的解决方案代码,您可以编写问题的答案并将其放在那里。