5

Games101透视投影矩阵推导

 2 years ago
source link: http://pkxpp.github.io/2022/08/12/games101%E9%80%8F%E8%A7%86%E6%8A%95%E5%BD%B1%E7%9F%A9%E9%98%B5%E6%8E%A8%E5%AF%BC/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

[toc]

在做作业的时候,发现和很多其他同学遇到一样的问题:旋转了90°。在作业3的小牛的时候,倒立的图像实在是看不过去了,就打算回头来研究一下这个问题了

y=x2y=x2

参考[1][2],先说一下问题原因:代码里面传过来的近平面和远平面都是正数,但是在课程视频里面说的n, f都是负数,看范围也可以看出来是是要写成[f, n]。

看了一下参考[1][2]中一些同学说的方法,把正负取反,以及乘以一个变换矩阵,都觉得怪怪的,所以手动推导一遍投影矩阵,直接应用这个公式看看。

透视投影矩阵推导

  • 坐标系:右手坐标系
right hand coordinate
  • 向量记法:列向量
V=⎛⎝⎜⎜⎜xyz1⎞⎠⎟⎟⎟V=(xyz1)
  • 摄像机是在原点,并且朝向z轴负方向看

这个会导致空间中任意一个要归一化标准立方体的范围是[l,r]x[b,t]x[f,n]

cube

简单思路就是,先把透视投影矩阵转换到正交投影矩阵

  • 正交投影矩阵

直接参考视频《Lecture 04 Transform Cont.》。先平移再缩放,这里因为是列向量表示法,所以是缩放矩阵x平移矩阵。

Mortho=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢2r−l00002t−b00002n−f00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢100001000010−r+l2−t+b2−n+f21⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥Mortho=[2r−l00002t−b00002n−f00001][100−r+l2010−t+b2001−n+f20001]
  • 齐次坐标空间性质

对于3D空间中任意一点(x, y, z),其次坐标乘以任意一个非0的实数都表示的是3D空间中的同一个点。即(x, y, z, 1) == (xz, yz, z^2, z != 0),这里乘以特殊的z也是满足这个性质的。

  • 透视投影视锥体上点投影到正交投影的立方体上 对于任意一点(x, y, z)投影到(x’, y’, z’)
  • y->y’
project y

可以很容易求出来(相似三角形)

y′=nzyy′=nzy
  • x->x’
project x
x′=nzxx′=nzx
  • 根据其次坐标空间性质,变换后的点都乘以z
⎛⎝⎜⎜⎜nx/zny/z?1⎞⎠⎟⎟⎟=⎛⎝⎜⎜⎜nxny?z⎞⎠⎟⎟⎟(nx/zny/z?1)=(nxny?z)

现在z不知道,接下来构建矩阵

Mpersp−>ortho⎛⎝⎜⎜⎜xyz1⎞⎠⎟⎟⎟=⎛⎝⎜⎜⎜nx/zny/z?z⎞⎠⎟⎟⎟=⎛⎝⎜⎜⎜nxny?z⎞⎠⎟⎟⎟Mpersp−>ortho(xyz1)=(nx/zny/z?z)=(nxny?z)
Mpersp−>ortho=⎡⎣⎢⎢⎢n0?00n?000?100?0⎤⎦⎥⎥⎥Mpersp−>ortho=[n0000n00????0010]
  • 根据两个特例来求上面这个矩阵

(1)对于近平面上的点,变换前后没有任何变化

(2)对于远平面上的中心点,变换前后没有变换

  • 近平面上的点满足条件

\(M_{persp->ortho}

⎛⎝⎜⎜⎜xyn1⎞⎠⎟⎟⎟(xyn1)

=

⎡⎣⎢⎢⎢n0?00n?000?100?0⎤⎦⎥⎥⎥[n0000n00????0010]
⎛⎝⎜⎜⎜xyn1⎞⎠⎟⎟⎟(xyn1)

=

⎛⎝⎜⎜⎜xyn1⎞⎠⎟⎟⎟(xyn1)

==

⎛⎝⎜⎜⎜nxnyn2n⎞⎠⎟⎟⎟(nxnyn2n)

\) 可知第三行前面两个和xy无关,但是可能和后面两个有关,设为A和B。所以矩阵可以写成如下形式:

\(M_{persp->ortho} =

⎡⎣⎢⎢⎢n0000n0000A100B0⎤⎦⎥⎥⎥[n0000n0000AB0010]

\) 可得第一个等式

An+B=n2An+B=n2
  • 对于远平面中心点
Mpersp−>ortho⎛⎝⎜⎜⎜00f1⎞⎠⎟⎟⎟=⎡⎣⎢⎢⎢n0000n0000A100B0⎤⎦⎥⎥⎥⎛⎝⎜⎜⎜00f1⎞⎠⎟⎟⎟=⎛⎝⎜⎜⎜00f1⎞⎠⎟⎟⎟==⎛⎝⎜⎜⎜00f2f⎞⎠⎟⎟⎟Mpersp−>ortho(00f1)=[n0000n0000AB0010](00f1)=(00f1)==(00f2f)

可得第二个等式

Af+B=f2Af+B=f2

根据两个等式可分别求得A和B:

A=n+fB=−nfA=n+fB=−nf
  • 先列出来fov和n的关系

因为f < n < 0,所以:

tan(β/2)=ratio−ntan(α/2)=1−ntan(β/2)=ratio−ntan(α/2)=1−n
h=1ratio=wh=wh=1ratio=wh=w

参考《3D Game Programming with DirectX 11》这本书里面的推导的概念,因为主要是关心纵横比(aspect ratio),所以可以简化的把h设置为1.

  • 正交投影矩阵表示
Mortho=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢2r−l00002t−b00002n−f00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢100001000010−r+l2−t+b2−n+f21⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002n−f00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢10000100001000−n+f21⎤⎦⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002n−f000−n+fn−f1⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥Mortho=[2r−l00002t−b00002n−f00001][100−r+l2010−t+b2001−n+f20001]=[1ratio0000100002n−f00001][10000100001−n+f20001]=[1ratio0000100002n−f−n+fn−f0001]
  • 最终的透视投影矩阵
Mpersp=MorthoMpersp−>ortho=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002n−f000−n+fn−f1⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢n0000n0000n+f100−nf0⎤⎦⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢nratio0000n0000n+fn−f100−2nfn−f0⎤⎦⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢−1tan(β/2)0000−rationtan(β/2)0000n+fn−f100−2nfn−f0⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥Mpersp=MorthoMpersp−>ortho=[1ratio0000100002n−f−n+fn−f0001][n0000n0000n+f−nf0010]=[nratio0000n0000n+fn−f−2nfn−f0010]=[−1tan(β/2)0000−rationtan(β/2)0000n+fn−f−2nfn−f0010]
  • bingo

这样结果虽然对了,但是其实是不合理的,因为传进来的f > n > 0是和这个推导不一致的。如果把-f, -n 代入到上面的矩阵,结果还是不对。代码里面应该还有哪个地方做了一次转换把这个问题给屏蔽掉了,后面继续看!

关于推导,看到上面就ok了,但是我在想左手和右手坐标系关系到底有多大,所以打算推一遍左手坐标系的

left hand coordinate
  • 行向量表示法
V=(x,y,z,1)V=(x,y,z,1)
  • 摄像机朝向z轴正方向看,立方体表示为[l, r]x[b,t]x[n,f]

注意这里f > n > 0

Mortho=⎡⎣⎢⎢⎢⎢⎢100−r+l2010−t+b2001−n+f20001⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢2r−l00002t−b00002f−n00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢10000100001−n+f20001⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002f−n00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002f−n−n+ff−n0001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥Mortho=[100001000010−r+l2−t+b2−n+f21][2r−l00002t−b00002f−n00001]=[10000100001000−n+f21][1ratio0000100002f−n00001]=[1ratio0000100002f−n000−n+ff−n1]
  • 透视投影矩阵

求出来的A和B跟右手坐标系是一样的结果:

A=n+fB=−nfA=n+fB=−nf

从这里可以看出来一点就是透视到正交的变换是跟坐标系无关的

Mpersp=Mpersp−>orthoMortho=⎡⎣⎢⎢⎢n0000n0000n+f100−nf0⎤⎦⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100002f−n−n+ff−n0001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢nratio0000n0000n+ff−n2nff−n0010⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢1tan(β/2)0000rationtan(β/2)0000n+ff−n2nff−n0010⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥Mpersp=Mpersp−>orthoMortho=[n0000n0000n+f−nf0010][1ratio0000100002f−n000−n+ff−n1]=[nratio0000n0000n+ff−n1002nff−n0]=[1tan(β/2)0000rationtan(β/2)0000n+ff−n1002nff−n0]

可以看到这里和《3D数学基础图形与游戏开发》这本书上的是一样的

matrix in 3D math

疑问:《3D Game Programming with DirectX 11》不一样是为什么?

书上推导出来的矩阵形式如下:

⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢1rtan(α/2)00001tan(α/2)0000ff−n−nff−n0010⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥[1rtan(α/2)00001tan(α/2)0000ff−n100−nff−n0]

参考书上5.6.3.4章节

原因主要是DX里面要求的立方体在z轴方向的范围是[0,1],而不是这里的[-1,1]。所以,把上面的正交投影矩阵改一下即可

  • 正交投影矩阵
Mortho=⎡⎣⎢⎢⎢⎢⎢100−r+l2010−t+b2001−n0001⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢2r−l00002t−b00001f−n00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢10000100001−n0001⎤⎦⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100001f−n00001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100001f−n−nf−n0001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥Mortho=[100001000010−r+l2−t+b2−n1][2r−l00002t−b00001f−n00001]=[10000100001000−n1][1ratio0000100001f−n00001]=[1ratio0000100001f−n000−nf−n1]
  • 透视投影矩阵
Mpersp=Mpersp−>orthoMortho=⎡⎣⎢⎢⎢n0000n0000n+f100−nf0⎤⎦⎥⎥⎥⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢1ratio0000100001f−n−nf−n0001⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢1tan(β/2)0000rationtan(β/2)0000ff−n−nff−n0010⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥Mpersp=Mpersp−>orthoMortho=[n0000n0000n+f−nf0010][1ratio0000100001f−n000−nf−n1]=[1tan(β/2)0000rationtan(β/2)0000ff−n100−nff−n0]

这样就和书上一致了。

[1]https://zhuanlan.zhihu.com/p/509902950

[2]https://games-cn.org/forums/forum/graphics-intro/page/3/

[3]https://www.bilibili.com/video/BV1X7411F744?p=4&vd_source=c10ae5c27bbde8ef3af23889645a0d8b

[4]《3D Game Programming with DirectX 11》

[5]《3D数学基础图形与游戏开发》


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK