参考[1][2],先说一下问题原因:代码里面传过来的近平面和远平面都是正数,但是在课程视频里面说的n, f都是负数,看范围也可以看出来是是要写成[f, n]。
看了一下参考[1][2]中一些同学说的方法,把正负取反,以及乘以一个变换矩阵,都觉得怪怪的,所以手动推导一遍投影矩阵,直接应用这个公式看看。
V = ( x y z 1 ) V = \begin{pmatrix} x \\ y \\ z \\ 1 \\ \end{pmatrix} V=⎝ ⎛xyz1⎠ ⎞
这个会导致空间中任意一个要归一化标准立方体的范围是[l,r]x[b,t]x[f,n]
简单思路就是,先把透视投影矩阵转换到正交投影矩阵
直接参考视频《Lecture 04 Transform Cont.》。先平移再缩放,这里因为是列向量表示法,所以是缩放矩阵x平移矩阵。
M o r t h o = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] [ 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − n + f 2 0 0 0 1 ] M_{ortho} = \begin{bmatrix} \dfrac{2}{r-l} & 0 & 0 & 0 \\ 0 & \dfrac{2}{t-b} & 0 & 0 \\ 0 & 0 & \dfrac{2}{n-f} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & -\dfrac{r+l}{2} \\ 0 & 1 & 0 & -\dfrac{t+b}{2} \\ 0 & 0 & 1 & -\dfrac{n+f}{2} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} Mortho=⎣ ⎡r−l20000t−b20000n−f200001⎦ ⎤⎣ ⎡100001000010−2r+l−2t+b−2n+f1⎦ ⎤
对于3D空间中任意一点(x, y, z),其次坐标乘以任意一个非0的实数都表示的是3D空间中的同一个点。即(x, y, z, 1) == (xz, yz, z^2, z != 0),这里乘以特殊的z也是满足这个性质的。
可以很容易求出来(相似三角形)
y ′ = n z y y' = \dfrac{n}{z}y y′=zny
同理可得
x ′ = n z x x' = \dfrac{n}{z}x x′=znx
( n x / z n y / z ? 1 ) = ( n x n y ? z ) \begin{pmatrix} nx/z \\ ny/z \\ ? \\ 1 \\ \end{pmatrix} = \begin{pmatrix} nx \\ ny \\ ? \\ z \\ \end{pmatrix} ⎝ ⎛nx/zny/z?1⎠ ⎞=⎝ ⎛nxny?z⎠ ⎞
现在z不知道,接下来构建矩阵
M p e r s p − > o r t h o ( x y z 1 ) = ( n x / z n y / z ? z ) = ( n x n y ? z ) M_{persp->ortho} \begin{pmatrix} x \\ y \\ z \\ 1 \\ \end{pmatrix} = \begin{pmatrix} nx/z \\ ny/z \\ ? \\ z \\ \end{pmatrix} = \begin{pmatrix} nx \\ ny \\ ? \\ z \\ \end{pmatrix} Mpersp−>ortho⎝ ⎛xyz1⎠ ⎞=⎝ ⎛nx/zny/z?z⎠ ⎞=⎝ ⎛nxny?z⎠ ⎞
M p e r s p − > o r t h o = [ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ] M_{persp->ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} Mpersp−>ortho=⎣ ⎡n0?00n?000?100?0⎦ ⎤
根据两个特例来求上面这个矩阵
(1)对于近平面上的点,变换前后没有任何变化
(2)对于远平面上的中心点,变换前后没有变换
近平面上的点满足条件
M p e r s p − > o r t h o ( x y n 1 ) = [ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ] ( x y n 1 ) = ( x y n 1 ) = = ( n x n y n 2 n ) M_{persp->ortho} \begin{pmatrix} x \\ y \\ n \\ 1 \\ \end{pmatrix} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \begin{pmatrix} x \\ y \\ n \\ 1 \\ \end{pmatrix} = \begin{pmatrix} x \\ y \\ n \\ 1 \\ \end{pmatrix}== \begin{pmatrix} nx \\ ny \\ n^2 \\ n \\ \end{pmatrix} Mpersp−>ortho⎝ ⎛xyn1⎠ ⎞=⎣ ⎡n0?00n?000?100?0⎦ ⎤⎝ ⎛xyn1⎠ ⎞=⎝ ⎛xyn1⎠ ⎞==⎝ ⎛nxnyn2n⎠ ⎞
可知第三行前面两个和xy无关,但是可能和后面两个有关,设为A和B。所以矩阵可以写成如下形式:
M p e r s p − > o r t h o = [ n 0 0 0 0 n 0 0 0 0 A B 0 0 1 0 ] M_{persp->ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} Mpersp−>ortho=⎣ ⎡n0000n0000A100B0⎦ ⎤
可得第一个等式
A n + B = n 2 An + B = n^2 An+B=n2
M p e r s p − > o r t h o ( 0 0 f 1 ) = [ n 0 0 0 0 n 0 0 0 0 A B 0 0 1 0 ] ( 0 0 f 1 ) = ( 0 0 f 1 ) = = ( 0 0 f 2 f ) M_{persp->ortho} \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \\ \end{pmatrix} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \\ \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \\ \end{pmatrix}== \begin{pmatrix} 0 \\ 0 \\ f^2 \\ f \\ \end{pmatrix} Mpersp−>ortho⎝ ⎛00f1⎠ ⎞=⎣ ⎡n0000n0000A100B0⎦ ⎤⎝ ⎛00f1⎠ ⎞=⎝ ⎛00f1⎠ ⎞==⎝ ⎛00f2f⎠ ⎞
可得第二个等式
A f + B = f 2 Af + B = f^2 Af+B=f2
根据两个等式可分别求得A和B:
A = n + f B = − n f \begin{aligned} & A = n + f \\ & B = -nf \\ \end{aligned} A=n+fB=−nf
因为f < n < 0,所以:
t a n ( β / 2 ) = r a t i o − n t a n ( α / 2 ) = 1 − n \begin{aligned} & tan(β/2) = \dfrac{ratio}{-n} \\ & tan(α/2) = \dfrac{1}{-n} \\ \end{aligned} tan(β/2)=−nratiotan(α/2)=−n1
其中:
h = 1 r a t i o = w h = w \begin{aligned} & h = 1 \\ & ratio = \dfrac{w}{h} = w \end{aligned} h=1ratio=hw=w
参考《3D Game Programming with DirectX 11》这本书里面的推导的概念,因为主要是关心纵横比(aspect ratio),所以可以简化的把h设置为1.
M o r t h o = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] [ 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − n + f 2 0 0 0 1 ] = [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 n − f 0 0 0 0 1 ] [ 1 0 0 0 0 1 0 0 0 0 1 − n + f 2 0 0 0 1 ] = [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 n − f − n + f n − f 0 0 0 1 ] \begin{aligned} M_{ortho} &= \begin{bmatrix} \dfrac{2}{r-l} & 0 & 0 & 0 \\ 0 & \dfrac{2}{t-b} & 0 & 0 \\ 0 & 0 & \dfrac{2}{n-f} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & -\dfrac{r+l}{2} \\ 0 & 1 & 0 & -\dfrac{t+b}{2} \\ 0 & 0 & 1 & -\dfrac{n+f}{2} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{n-f} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & -\dfrac{n+f}{2} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & =\begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{n-f} & -\dfrac{n+f}{n-f} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ \end{aligned} Mortho=⎣ ⎡r−l20000t−b20000n−f200001⎦ ⎤⎣ ⎡100001000010−2r+l−2t+b−2n+f1⎦ ⎤=⎣ ⎡ratio1000010000n−f200001⎦ ⎤⎣ ⎡10000100001000−2n+f1⎦ ⎤=⎣ ⎡ratio1000010000n−f2000−n−fn+f1⎦ ⎤
M p e r s p = M o r t h o M p e r s p − > o r t h o = [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 n − f − n + f n − f 0 0 0 1 ] [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] = [ n r a t i o 0 0 0 0 n 0 0 0 0 n + f n − f − 2 n f n − f 0 0 1 0 ] = [ − 1 t a n ( β / 2 ) 0 0 0 0 − r a t i o n t a n ( β / 2 ) 0 0 0 0 n + f n − f − 2 n f n − f 0 0 1 0 ] \begin{aligned} M_{persp} &= M_{ortho}M_{persp->ortho} \\ &= \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{n-f} & -\dfrac{n+f}{n-f} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -nf \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{n}{ratio} & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & \dfrac{n+f}{n-f} & -\dfrac{2nf}{n-f} \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{-1}{tan(β/2)} & 0 & 0 & 0 \\ 0 & -\dfrac{ration}{tan(β/2)} & 0 & 0 \\ 0 & 0 & \dfrac{n+f}{n-f} & -\dfrac{2nf}{n-f} \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \end{aligned} Mpersp=MorthoMpersp−>ortho=⎣ ⎡ratio1000010000n−f2000−n−fn+f1⎦ ⎤⎣ ⎡n0000n0000n+f100−nf0⎦ ⎤=⎣ ⎡ration0000n0000n−fn+f100−n−f2nf0⎦ ⎤=⎣ ⎡tan(β/2)−10000−tan(β/2)ration0000n−fn+f100−n−f2nf0⎦ ⎤
这样结果虽然对了,但是其实是不合理的,因为传进来的f > n > 0是和这个推导不一致的。如果把-f, -n 代入到上面的矩阵,结果还是不对。代码里面应该还有哪个地方做了一次转换把这个问题给屏蔽掉了,后面继续看!
关于推导,看到上面就ok了,但是我在想左手和右手坐标系关系到底有多大,所以打算推一遍左手坐标系的
V = ( x , y , z , 1 ) V = \begin{pmatrix} x , y , z , 1 \end{pmatrix} V=(x,y,z,1)
注意这里f > n > 0
M o r t h o = [ 1 0 0 0 0 1 0 0 0 0 1 0 − r + l 2 − t + b 2 − n + f 2 1 ] [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 f − n 0 0 0 0 1 ] = [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 − n + f 2 1 ] [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 f − n 0 0 0 0 1 ] = [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 f − n 0 0 0 − n + f f − n 1 ] \begin{aligned} M_{ortho} &= \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ -\dfrac{r+l}{2} & -\dfrac{t+b}{2} & -\dfrac{n+f}{2} & 1 \\ \end{bmatrix} \begin{bmatrix} \dfrac{2}{r-l} & 0 & 0 & 0 \\ 0 & \dfrac{2}{t-b} & 0 & 0 \\ 0 & 0 & \dfrac{2}{f-n} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & -\dfrac{n+f}{2} & 1 \\ \end{bmatrix} \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{f-n} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & =\begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{f-n} & 0 \\ 0 & 0 & -\dfrac{n+f}{f-n} & 1 \\ \end{bmatrix} \\ \end{aligned} Mortho=⎣ ⎡100−2r+l010−2t+b001−2n+f0001⎦ ⎤⎣ ⎡r−l20000t−b20000f−n200001⎦ ⎤=⎣ ⎡10000100001−2n+f0001⎦ ⎤⎣ ⎡ratio1000010000f−n200001⎦ ⎤=⎣ ⎡ratio1000010000f−n2−f−nn+f0001⎦ ⎤
求出来的A和B跟右手坐标系是一样的结果:
A = n + f B = − n f \begin{aligned} & A = n + f \\ & B = -nf \\ \end{aligned} A=n+fB=−nf
从这里可以看出来一点就是透视到正交的变换是跟坐标系无关的
M p e r s p = M p e r s p − > o r t h o M o r t h o = [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] [ 1 r a t i o 0 0 0 0 1 0 0 0 0 2 f − n 0 0 0 − n + f f − n 1 ] = [ n r a t i o 0 0 0 0 n 0 0 0 0 n + f f − n 1 0 0 2 n f f − n 0 ] = [ 1 t a n ( β / 2 ) 0 0 0 0 r a t i o n t a n ( β / 2 ) 0 0 0 0 n + f f − n 1 0 0 2 n f f − n 0 ] \begin{aligned} M_{persp} &= M_{persp->ortho}M_{ortho} \\ &= \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -nf \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{2}{f-n} & 0 \\ 0 & 0 & -\dfrac{n+f}{f-n} & 1 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{n}{ratio} & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & \dfrac{n+f}{f-n} & 1 \\ 0 & 0 & \dfrac{2nf}{f-n} & 0 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{1}{tan(β/2)} & 0 & 0 & 0 \\ 0 & \dfrac{ration}{tan(β/2)} & 0 & 0 \\ 0 & 0 & \dfrac{n+f}{f-n} & 1 \\ 0 & 0 & \dfrac{2nf}{f-n} & 0 \\ \end{bmatrix} \end{aligned} Mpersp=Mpersp−>orthoMortho=⎣ ⎡n0000n0000n+f100−nf0⎦ ⎤⎣ ⎡ratio1000010000f−n2−f−nn+f0001⎦ ⎤=⎣ ⎡ration0000n0000f−nn+ff−n2nf0010⎦ ⎤=⎣ ⎡tan(β/2)10000tan(β/2)ration0000f−nn+ff−n2nf0010⎦ ⎤
可以看到这里和《3D数学基础图形与游戏开发》这本书上的是一样的
书上推导出来的矩阵形式如下:
[ 1 r t a n ( α / 2 ) 0 0 0 0 1 t a n ( α / 2 ) 0 0 0 0 f f − n 1 0 0 − n f f − n 0 ] \begin{bmatrix} \dfrac{1}{rtan(α/2)} & 0 & 0 & 0 \\ 0 & \dfrac{1}{tan(α/2)} & 0 & 0 \\ 0 & 0 & \dfrac{f}{f-n} & 1 \\ 0 & 0 & -\dfrac{nf}{f-n} & 0 \\ \end{bmatrix} ⎣ ⎡rtan(α/2)10000tan(α/2)10000f−nf−f−nnf0010⎦ ⎤
参考书上5.6.3.4章节
原因主要是DX里面要求的立方体在z轴方向的范围是[0,1],而不是这里的[-1,1]。所以,把上面的正交投影矩阵改一下即可
M o r t h o = [ 1 0 0 0 0 1 0 0 0 0 1 0 − r + l 2 − t + b 2 − n 1 ] [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 1 f − n 0 0 0 0 1 ] = [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 − n 1 ] [ 1 r a t i o 0 0 0 0 1 0 0 0 0 1 f − n 0 0 0 0 1 ] = [ 1 r a t i o 0 0 0 0 1 0 0 0 0 1 f − n 0 0 0 − n f − n 1 ] \begin{aligned} M_{ortho} &= \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ -\dfrac{r+l}{2} & -\dfrac{t+b}{2} & -n & 1 \\ \end{bmatrix} \begin{bmatrix} \dfrac{2}{r-l} & 0 & 0 & 0 \\ 0 & \dfrac{2}{t-b} & 0 & 0 \\ 0 & 0 & \dfrac{1}{f-n} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & -n & 1 \\ \end{bmatrix} \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{1}{f-n} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \\ & =\begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{1}{f-n} & 0 \\ 0 & 0 & -\dfrac{n}{f-n} & 1 \\ \end{bmatrix} \\ \end{aligned} Mortho=⎣ ⎡100−2r+l010−2t+b001−n0001⎦ ⎤⎣ ⎡r−l20000t−b20000f−n100001⎦ ⎤=⎣ ⎡10000100001−n0001⎦ ⎤⎣ ⎡ratio1000010000f−n100001⎦ ⎤=⎣ ⎡ratio1000010000f−n1−f−nn0001⎦ ⎤
M p e r s p = M p e r s p − > o r t h o M o r t h o = [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] [ 1 r a t i o 0 0 0 0 1 0 0 0 0 1 f − n 0 0 0 − n f − n 1 ] = [ 1 t a n ( β / 2 ) 0 0 0 0 r a t i o n t a n ( β / 2 ) 0 0 0 0 f f − n 1 0 0 − n f f − n 0 ] \begin{aligned} M_{persp} &= M_{persp->ortho}M_{ortho} \\ &= \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -nf \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \begin{bmatrix} \dfrac{1}{ratio} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \dfrac{1}{f-n} & 0 \\ 0 & 0 & -\dfrac{n}{f-n} & 1 \\ \end{bmatrix} \\ & = \begin{bmatrix} \dfrac{1}{tan(β/2)} & 0 & 0 & 0 \\ 0 & \dfrac{ration}{tan(β/2)} & 0 & 0 \\ 0 & 0 & \dfrac{f}{f-n} & 1 \\ 0 & 0 & -\dfrac{nf}{f-n} & 0 \\ \end{bmatrix} \end{aligned} Mpersp=Mpersp−>orthoMortho=⎣ ⎡n0000n0000n+f100−nf0⎦ ⎤⎣ ⎡ratio1000010000f−n1−f−nn0001⎦ ⎤=⎣ ⎡tan(β/2)10000tan(β/2)ration0000f−nf−f−nnf0010⎦ ⎤
这样就和书上一致了。
[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数学基础图形与游戏开发》