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;
这意味着通过将cameraspace
与Proj
的第四行进行点积运算,可以计算出clipspace.w
。
如果我们查看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是非零数字。暂时忽略这些数字的含义。因此,clippsace.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;