如何在不使用矩阵乘法的情况下从屏幕空间位置计算视图空间位置

4

我正在计算多个光线和后处理着色器中屏幕空间位置的视图空间位置。 我目前的代码使用投影矩阵的逆来还原位置:

float depth = depthBuffer.Sample(sampler, uv).x;
float4 temp = mul(float4(uv.x * 2 - 1, (1 - uv.y) * 2 - 1, depth, 1), inverseProjectionMatrix);
float3 viewSpacePosition = temp.xyz / temp.w;

作为一项优化,我希望从我的着色器中删除矩阵乘法,并使用变量(如aspectnear-clipfar-clip)恢复视图空间位置。

2
你怎么知道去掉矩阵乘法就会更快呢?GPU基本上是为此设计的,而且只涉及一些简单的乘加运算。 - BonzaiThePenguin
这是16次乘法,其中只有5次是必需的(因为投影矩阵的其他11个字段被清零)。场景中有大量灯光,所有灯光都在像素着色器中使用上述代码,并且还在全屏效果(如SSAO)中使用(每像素多次)。我不知道这会对性能产生多大影响,但渲染器目前受限于GPU,而且这段代码非常常见,所以我想试一试 :) - Jens Nolte
即使是一个天真的向量-矩阵乘法也仅仅包含四个矢量化的乘加操作,而且GPU通常会针对矩阵具有大量零元素(即稀疏 vs. 密集)时确定优化路径。 - BonzaiThePenguin
GPU如何在编译前不知道零的情况下采取优化路径?此外,四个MAD只有我的点光源着色器周期的一半,即使性能提升很小,我仍然对数学感兴趣。 - Jens Nolte
1
@JensNolte 在一个周期内,GPU硬件核心可以并行测试一行中的所有值,并在看到单个1和所有其他元素为0时执行移动而不是点积。 - Gene
1个回答

3
您没有说明如何创建透视投影矩阵,这是关键信息。我不是D3D专家,而是了解OpenGL。
因此,我可以向您展示如何使用OpenGL的gluPerspective生成的矩阵来完成此操作。它的形式为:

gluPerspective matrix

这个矩阵是用于OpenGL列向量的。你的D3D代码使用的是行向量,所以我会假设进行转置操作。为了方便起见,重命名非零项并取其逆:

gluPerspective simplified and inverted

使用Wolfram Alpha进行符号求逆(这是一个非常好的工具):
{{a,0,0,0},{0,b,0,0},{0,0,c,-1},{0,0,d,0}}^-1

我们得到:

inverse matrix expression

所以,如果要乘的点是[x y z 1],就像你的代码中一样,那么你想要的结果就是:
[x/a y/b -1 (z+c)/d]

这是一个四维齐次点。为了回到三维,需要通过w除以x、y、z:
[(1/a)ux  (1/b)uy  -u]  where u = d / (z+c)

(1/a)和(1/b)项当然可以提前计算。之后,您需要进行一次加法、一次除法和四次乘法即可完成任务。
由于D3D和OpenGL坐标系统之间的差异,本说明可能存在一些负号或其他细节上的偏差,我不太清楚。
话虽如此,我同意那些认为这不会产生有意义的加速的评论者的看法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接