DirectX投影变换矩阵的原理与推导,齐次裁剪空间的应用举例

来源:互联网 发布:润和软件工资待遇 编辑:程序博客网 时间:2024/06/05 14:25

DirectX Geometry Pipeline的投影变换不是D3DXMatrixShadow或者几何书上的投影变换。
几何书上的投影变换是这样的:

分别是平行投影和透视投影,把空间中的三维物体变成二维平面上的图形。
而DirectX Geometry Pipeline的投影变换是这样的:

上图分别是 Perspective CameraOrthographic Camera 的视景体1(图中的坐标架代表摄像机坐标系),不同的视景体决定了不同的投影类型,常见的是透视投影和正投影。DirectX Geometry Pipeline的投影变换是把视景体都变成DirectX规定的长方体 H(齐次裁剪空间):
这里写图片描述
w>0:2
齐次坐标

P=(x,y,z,w)Hx/w[1,1]y/w[1,1]z/w[0,1]x[w,w]y[w,w]z[0,w]

前者是三维到二维的变换,后者是三维到三维的变换;
后者去掉深度信息(z坐标)就相当于前者;
前者补上深度信息可以得到后者。

Perspective Camera

现在来求 把Perspective Camera的视景体变成DirectX规定的长方体的透视矩阵,
思路是先计算三维到二维的透视投影,再加上深度向量。
这里写图片描述
E是透视投影线的汇聚点,P’ 位于投影平面上,该平面过点Q,法向是N(单位法向量)朝右,
由质点几何易知P’的齐次坐标为((PQ)NE+(QE)NP,(PE)N)
深度向量 d=((PQ)NN,0).

要还原P’的深度信息,只需 把P’的位置坐标加上深度向量d 即可

((PQ)NE+(QE)NP(PE)N,1)+((PQ)NN,0)=

((PQ)NE+(QE)NP(PE)N+(PQ)NN,1)

((PQ)NE+(QE)NP+(PE)N(PQ)NN,(PE)N)

但是因为含有(PE)N(PQ)NN,无法写成对 (P,1)的线性变换。这样我们不得不放弃使用真正的深度值了。

深度缓冲区是用来进行深度测试,处理物体的遮挡的。只要能保持相对深度关系,即使不是真正的深度值也没有关系。
如果我们把P’的齐次坐标和深度向量 d 直接相加:

((PQ)NE+(QE)NP,(PE)N)+((PQ)NN,0)=

((PQ)NE+(QE)NP+(PQ)NN,(PE)N)=

[P(NE+(QE)NI+NN)QN(E+N),PNEN]=

(P,1)[NE+(QE)NI+NNQN(E+N)NEN](1)

得到的这个矩阵叫 伪透视矩阵,它把[0,+)的实际深度值映射成[0,1],如下图:
这里写图片描述

zn 代表近裁剪平面(相对摄像机原点)的位置,x代表实际深度值(到近裁剪平面的距离),f(x)代表伪深度值。可见,太远的深度值会产生精度上的问题。
对于Perspective Camera的视景体,伪透视矩阵 把[0,zfzn]的深度值映射成[0,zfznzf].

现在把 E=(0,0,0),N=(0,0,1),Q=(0,0,zn)=znN 代入,伪透视矩阵 =

[znI+NNznNN0]=zn0000zn0000zn+1zn0010(2)

假设w、h分别代表近裁剪平面的宽度和高度,那么上面这个伪透视矩阵就把Perspective Camera的视景体变成了whzfznzf的长方体,不过,摄像机坐标架的位置还没变。

接下来要做的就是把这个whzfznzf的长方体变成DirectX规定的”宽2高2深1“的长方体,并且把摄像机坐标架平移到下图所示的位置。
这里写图片描述
所以,s=zfzfzn

zn0000zn0000zn+1zn00102w00002h0000sszn0001=

2znw00002znh0000sszn0010M(s=zfzfzn)(3)

这就是DirectX Perspective Camera的透视矩阵。它的第3行第4列的元素是1,所以若P=(x,y,z,1)是摄像机坐标系中的点,则PM=(x,y,z,z). 这样就能直接用变换后点的 w 坐标当做深度信息,而不用z/z。即 w-based depth buffer, or w-buffer3.
所以,虽然上面矩阵乘以任意的非零常数 得到的也都是透视矩阵,但是见脚注4。

Orthographic Camera

2w00002h00001zfznzn1zfzn0001(4)

先缩放,再平移。



摄像机投影之后,下一步见脚注5。
参考读物《计算机图形学与几何造型导论》一本有趣的书,翻译得也很流畅。


最后说一下齐次裁剪空间的应用:
  齐次裁剪空间的概念不光是理解DirectX投影变换的前提,还可以用它做很多有意思的事情,比如:你可能需要从某个位置某个角度摄像机的渲染结果中“抠出”某块区域(portal)内的像,用来渲染平面镜成像、虫洞(portal rendering),也可以用来渲染小地图(正投影摄像机)等。
 “抠出”是个形象的说法,不是真的去抠图,其实是把摄像机第一次的渲染结果Render to Texture,作为第二次渲染时 portal 的shader resource,用portal 的纹理坐标去采样,而 portal 的纹理坐标哪里来?就是从齐次裁剪空间来的:
这里写图片描述
(uv)=12[(xy)+(0.50.5)]

还有,3D程序的鼠标拾取算法:先根据鼠标的屏幕位置坐标构造摄像机空间的射线,再求交。


  1. Perspective Camera的视景体是一个四棱台,由垂直于z轴的远、近裁剪平面,过x轴 相对xoz平面对称的两个平面,过y轴 相对yoz平面对称的两个平面 围成;
    Orthographic Camera的视景体是一个长方体,由”近、远、上、下、左、右“六个面围成;z轴正向朝远,x轴正向朝右,y轴正向朝上,且长方体沿z轴旋转对称。 ↩
  2. Viewports and Clipping (Direct3D 9) ↩
  3. Depth Buffers (Direct3D 9)
    https://www.mvps.org/directx/articles/using_w-buffers.htm ↩
  4. A W-Friendly Projection Matrix ↩
  5. Viewports and Clipping (Direct3D 9) ↩
1 0