参考may佬《技术美术百人计划》
冯乐乐《Unity Shader入门精要》
写给VR手游开发小白的教程:(四)补充篇,详细介绍Unity中相机的投影矩阵
投影矩阵的推导(Deriving Projection Matrices)
Eric Lengyel. Mathematics for 30 game programming and computer graphics (3rd Edition). 2011 by Charles River Media
图形1.2.3 MVP矩阵变换
变换矩阵
- 将3D物体转化到2D平面
- 为各个空间的运用做准备
MVP矩阵
MVP矩阵分别是模型(Model)矩阵、观察(View)矩阵、投影(Projection)矩阵。
坐标空间
- 因为一些概念只有在特定的坐标空间下才有意义,才更容易理解,所以我们需要在不同的情况下使用不同的坐标空间。
- 顶点的起始坐标位于局部空间(模型空间),在这里被称作局部坐标(Local Coordinate),之后会变为世界坐标(World Coordinate)、观察坐标(View Coordinate)、裁剪坐标(Clip Coordinate),最后以屏幕坐标(Screen Coordinate)结束。
M矩阵
模型空间 → \rightarrow →世界空间
- 将顶点坐标从模型空间变换到世界空间的过程被称作模型变换
- 模型变换依次进行了缩放、旋转、平移的操作,对应操作依次进行矩阵变换即可得出模型变换的变换矩阵,即M矩阵(注意矩阵的左乘顺序)
- M m o d e l = [ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] [ cos θ 0 sin θ 0 0 1 0 0 − sin θ 0 cos θ 0 0 0 0 1 ] [ k x 0 0 0 0 k y 0 0 0 0 k z 0 0 0 0 1 ] M_{model}=\begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} k_x & 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 &k_ z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Mmodel=⎣⎢⎢⎡100001000010txtytz1⎦⎥⎥⎤⎣⎢⎢⎡cosθ0−sinθ00100sinθ0cosθ00001⎦⎥⎥⎤⎣⎢⎢⎡kx0000ky0000kz00001⎦⎥⎥⎤
- P w o r l d = M m o d e l P m o d e l P_{world}=M_{model}P_{model} Pworld=MmodelPmodel
V矩阵
世界空间 → \rightarrow →观察空间
- 将顶点坐标从世界空间变换到观察空间,被叫做观察变换
- 变换步骤:
- 平移整个观察空间,让摄像机原点位于世界坐标的原点,坐标轴与世界空间中的坐标轴重合
- 摄像机在世界空间中是先旋转再平移的,因此为了将摄像机移回初始状态(位于原点,坐标轴重合),需要进行逆变换
- 在Unity中模型空间和世界空间都是左手系,观察空间却是右手系,z轴方向相反,因此需要对z取反
- 所以观察变换的顺序为先平移,再旋转,最后对z分量取反
- M v i e w = [ 1 0 0 0 0 1 0 0 0 0 − 1 0 0 0 0 0 ] [ 1 0 0 0 0 cos θ − sin θ 0 0 sin θ cos θ 0 0 0 0 1 ] [ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] M_{view}=\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -\sin \theta & 0 \\ 0 & \sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 &1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} Mview=⎣⎢⎢⎡1000010000−100000⎦⎥⎥⎤⎣⎢⎢⎡10000cosθsinθ00−sinθcosθ00001⎦⎥⎥⎤⎣⎢⎢⎡100001000010txtytz1⎦⎥⎥⎤
- P v i e w = M v i e w P w o r l d P_{view}=M_{view}P_{world} Pview=MviewPworld
P矩阵
观察空间 → \rightarrow →裁剪空间
-
用于判断顶点是否在可见范围内,为投影做准备(虽然叫做投影变换,但是并没有真正进行投影工作)
-
位于视锥体内的图元保留,视锥体之外的图元将被剔除,与裁剪平面相交的图元会被裁剪
- 为什么要进行投影变换?
- 为什么要进行投影变换?
-
透视投影
- 根据Near和Far以及视场角FOV可以求出视锥体近裁剪平面和远裁剪平面的高度:
- n e a r C l i p P l a n e H e i g h t = 2 ⋅ N e a r ⋅ tan F O V 2 nearClipPlaneHeight=2·Near·\tan\frac{FOV}{2} nearClipPlaneHeight=2⋅Near⋅tan2FOV
- f a r C l i p P l a n e H e i g h t = 2 ⋅ F a r ⋅ tan F O V 2 farClipPlaneHeight=2·Far·\tan\frac{FOV}{2} farClipPlaneHeight=2⋅Far⋅tan2FOV
- 根据摄像机的纵横比Aspect得到裁剪平面的宽度
- A s p e c t = n e a r C l i p P l a n e W i d t h n e a r C l i p P l a n e H e i g h t Aspect=\frac{nearClipPlaneWidth}{nearClipPlaneHeight} Aspect=nearClipPlaneHeightnearClipPlaneWidth
- A s p e c t = f a r C l i p P l a n e W i d t h f a r C l i p P l a n e H e i g h t Aspect=\frac{farClipPlaneWidth}{farClipPlaneHeight} Aspect=farClipPlaneHeightfarClipPlaneWidth
- M f r u s t u m = [ cot F O V 2 A s p e c t 0 0 0 0 cot F O V 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] M_{frustum}=\begin{bmatrix} \frac{\cot\frac{FOV}{2}}{Aspect} & 0 & 0 & 0 \\ 0 & \cot\frac{FOV}{2} & 0 & 0 \\ 0 & 0 & -\frac{Far+Near}{Far-Near} & -\frac{2·Near·Far}{Far-Near} \\ 0 & 0 & -1 & 0 \end{bmatrix} Mfrustum=⎣⎢⎢⎢⎡Aspectcot2FOV0000cot2FOV0000−Far−NearFar+Near−100−Far−Near2⋅Near⋅Far0⎦⎥⎥⎥⎤
- P c l i p = M f r u s t u m P v i e w = [ x cot F O V 2 A s p e c t y cot F O V 2 − z F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r − z ] P_{clip}=M_{frustum}P_{view}=\begin{bmatrix} x\frac{\cot\frac{FOV}{2}}{Aspect} \\ y\cot\frac{FOV}{2} \\ -z\frac{Far+Near}{Far-Near}-\frac{2·Near·Far}{Far-Near} \\ -z \end{bmatrix} Pclip=MfrustumPview=⎣⎢⎢⎢⎡xAspectcot2FOVycot2FOV−zFar−NearFar+Near−Far−Near2⋅Near⋅Far−z⎦⎥⎥⎥⎤
- 可以看出,投影矩阵本质是对 x x x、 y y y、 z z z分量进行了不同程度的缩放(同时对 z z z分量进行了平移),此时顶点的 w w w分量不再是1,而是原先 z z z分量的取反。
- 如果一个顶点在视锥体内,那么它变换后的坐标需满足:
− w ≤ x ≤ w -w\leq x\leq w −w≤x≤w
− w ≤ y ≤ w -w\leq y\leq w −w≤y≤w
− w ≤ z ≤ w -w\leq z\leq w −w≤z≤w
- 根据Near和Far以及视场角FOV可以求出视锥体近裁剪平面和远裁剪平面的高度:
-
正交投影
- 根据Near和Far以及Size可以得到裁剪平面高度
- n e a r C l i p P l a n e H e i g h t = f a r C l i p P l a n e H e i g h t = 2 ⋅ S i z e nearClipPlaneHeight=farClipPlaneHeight=2·Size nearClipPlaneHeight=farClipPlaneHeight=2⋅Size
- 根据摄像机纵横比Aspect得到裁剪平面宽度
- f a r C l i p P l a n e W i d t h = n e a r C l i p P l a n e W i d t h = A s p e c t ⋅ n e a r C l i p P l a n e H e i g h t farClipPlaneWidth=nearClipPlaneWidth=Aspect·nearClipPlaneHeight farClipPlaneWidth=nearClipPlaneWidth=Aspect⋅nearClipPlaneHeight
- M f r u s t u m = [ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] M_{frustum}=\begin{bmatrix} \frac{1}{Aspect·Size} & 0 & 0 & 0 \\ 0 & \frac{1}{Size} & 0 & 0 \\ 0 & 0 & -\frac{2}{Far-Near} & -\frac{Far+Near}{Far-Near} \\ 0 & 0 & 0 & 1 \end{bmatrix} Mfrustum=⎣⎢⎢⎡Aspect⋅Size10000Size10000−Far−Near2000−Far−NearFar+Near1⎦⎥⎥⎤
- P c l i p = M o r t h o P v i e w = [ x A s p e c t ⋅ S i z e ⋅ y S i z e − 2 z F a r − N e a r − F a r + N e a r F a r − N e a r 1 ] P_{clip}=M_{ortho}P_{view}=\begin{bmatrix} \frac{x}{Aspect·Size·} \\ \frac{y}{Size} \\ -\frac{2z}{Far-Near}-\frac{Far+Near}{Far-Near} \\ 1 \end{bmatrix} Pclip=MorthoPview=⎣⎢⎢⎡Aspect⋅Size⋅xSizey−Far−Near2z−Far−NearFar+Near1⎦⎥⎥⎤
- 如果一个顶点在视锥体内,那么它变换后的坐标需满足:
− w ≤ x ≤ w -w\leq x\leq w −w≤x≤w
− w ≤ y ≤ w -w\leq y\leq w −w≤y≤w
− w ≤ z ≤ w -w\leq z\leq w −w≤z≤w
- 根据Near和Far以及Size可以得到裁剪平面高度
P矩阵推导
先从正交投影开始
为了方便计算,定义视锥体的范围为从
(
l
,
b
,
n
)
(l,b,n)
(l,b,n)到
(
r
,
t
,
f
)
(r,t,f)
(r,t,f)
根据这张图可知,正交投影将视锥体的范围映射到(-1,-1,-1)至(1,1,1),即将三个坐标的范围变换至
[
−
1
,
1
]
[-1,1]
[−1,1]
从 x x x轴开始
l ≤ x ≤ r l\leq x\leq r l≤x≤r
经过一系列变换将中间项变成形如 p x + q px+q px+q的形式
0 ≤ x − l ≤ r − l 0\leq x-l\leq r-l 0≤x−l≤r−l
− 1 ≤ 2 ( x − l ) r − l − 1 ≤ 1 -1\leq \frac{2(x-l)}{r-l}-1\leq 1 −1≤r−l2(x−l)−1≤1
− 1 ≤ 2 x r − l − r + l r − l ≤ 1 -1\leq \frac{2x}{r-l}-\frac{r+l}{r-l}\leq 1 −1≤r−l2x−r−lr+l≤1
这个中间项即转换 x x x的公式
x ′ = 2 x r − l − r + l r − l x'=\frac{2x}{r-l}-\frac{r+l}{r-l} x′=r−l2x−r−lr+l
同理可得 y y y的转换公式
y ′ = 2 y t − b − t + b t − b y'=\frac{2y}{t-b}-\frac{t+b}{t-b} y′=t−b2y−t−bt+b
需要注意的是,视锥体经过转换之后从右手系转换到了左手系,因此 z z z是从 [ − f , − n ] [-f,-n] [−f,−n]映射到 [ − 1 , 1 ] [-1,1] [−1,1]
tips:OpenGL的z分量映射到[-1,1],DirectX的z分量则是映射到[0,1]
− n ≤ z ≤ − f -n\leq z\leq -f −n≤z≤−f
− 1 ≤ − 2 z f − n − f + n f − n ≤ 1 -1\leq -\frac{2z}{f-n}-\frac{f+n}{f-n}\leq 1 −1≤−f−n2z−f−nf+n≤1
得到 z z z的转换公式
z ′ = − 2 z f − n − f + n f − n z'=-\frac{2z}{f-n}-\frac{f+n}{f-n} z′=−f−n2z−f−nf+n
w分量为1,此时的转换矩阵为
P o = [ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 − 2 z f − n − f + n f − n 0 0 0 1 ] P_o=\begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & -\frac{2z}{f-n} & -\frac{f+n}{f-n} \\ 0 & 0 & 0 & 1 \end{bmatrix} Po=⎣⎢⎢⎡r−l20000t−b20000−f−n2z0−r−lr+l−t−bt+b−f−nf+n1⎦⎥⎥⎤
由于z轴会穿过视锥体的中心,因此 r = − l , t = − b r=-l,t=-b r=−l,t=−b,通过摄像机组件可以获得以下信息
n e a r C l i p P l a n e H e i g h t = f a r C l i p P l a n e H e i g h t = 2 ⋅ S i z e nearClipPlaneHeight=farClipPlaneHeight=2·Size nearClipPlaneHeight=farClipPlaneHeight=2⋅Size
f a r C l i p P l a n e W i d t h = n e a r C l i p P l a n e W i d t h = A s p e c t ⋅ n e a r C l i p P l a n e H e i g h t farClipPlaneWidth=nearClipPlaneWidth=Aspect·nearClipPlaneHeight farClipPlaneWidth=nearClipPlaneWidth=Aspect⋅nearClipPlaneHeight
代入之后可得到正交投影矩阵
M f r u s t u m = [ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] M_{frustum}=\begin{bmatrix} \frac{1}{Aspect·Size} & 0 & 0 & 0 \\ 0 & \frac{1}{Size} & 0 & 0 \\ 0 & 0 & -\frac{2}{Far-Near} & -\frac{Far+Near}{Far-Near} \\ 0 & 0 & 0 & 1 \end{bmatrix} Mfrustum=⎣⎢⎢⎡Aspect⋅Size10000Size10000−Far−Near2000−Far−NearFar+Near1⎦⎥⎥⎤
接下来是透视投影,对 x , y x,y x,y的处理可以分两步:
- 给定视锥体中的点 ( x , y , z ) (x,y, z) (x,y,z),把它投影到近平面 z = n z=n z=n。由于投影点在近平面上,所以它的 x x x坐标范围在 [ l , r ] [l, r] [l,r], y y y坐标范围在 [ b , t ] [b, t] [b,t]。
- 使用在正交投影中推导的公式,把 x x x坐标从 [ l , r ] [l, r] [l,r]映射到 [ − 1 , 1 ] [-1, 1] [−1,1],把 y y y坐标范围从 [ b , t ] [b, t] [b,t]映射到 [ − 1 , 1 ] [-1, 1] [−1,1]。
根据相似三角形,可知
x
x
x坐标为
x
n
z
\frac{xn}{z}
zxn,
y
y
y坐标为
y
n
z
\frac{yn}{z}
zyn
将以上坐标带入之前推导的公式中
x ′ = ( 2 n r − l ) x z − r + l r − l x'=(\frac{2n}{r-l})\frac{x}{z}-\frac{r+l}{r-l} x′=(r−l2n)zx−r−lr+l
y ′ = ( 2 n t − b ) y z − t + b t − b y'=(\frac{2n}{t-b})\frac{y}{z}-\frac{t+b}{t-b} y′=(t−b2n)zy−t−bt+b
消去分母上的 z z z
x ′ z = ( 2 n r − l ) x − r + l r − l z x'z=(\frac{2n}{r-l})x-\frac{r+l}{r-l}z x′z=(r−l2n)x−r−lr+lz
y ′ z = ( 2 n t − b ) y − t + b t − b z y'z=(\frac{2n}{t-b})y-\frac{t+b}{t-b}z y′z=(t−b2n)y−t−bt+bz
为了将等式写入矩阵,需要转化成形如 x ′ = c 1 x + c 2 y + c 3 z + c 4 x'=c_1x+c_2y+c_3z+c_4 x′=c1x+c2y+c3z+c4的格式,为此需要获得一个相似的 z ′ z z'z z′z的公式,然后就可以通过一个变换矩阵将 ( x , y , z ) (x,y,z) (x,y,z)映射到 ( x ′ z , y ′ z , z ′ z ) (x'z,y'z,z'z) (x′z,y′z,z′z),再将各部分除以 z z z即可得到需要的 ( x ′ , y ′ , z ′ ) (x',y',z') (x′,y′,z′)
为此需要一个公式形如 z ′ z = p z + q z'z=pz+q z′z=pz+q,已知需要把 [ − f , − n ] [-f,-n] [−f,−n]映射到 [ − 1 , 1 ] [-1,1] [−1,1]因此当 z = − f z=-f z=−f时 z ′ = 1 z'=1 z′=1, z = − n z=-n z=−n时 z ′ = − 1 z'=-1 z′=−1,可得方程组
tips:此处映射关系要注意,-f , -n ---> -1 , 1
| |_______| |
|___________________|
−
f
=
p
f
+
q
-f=pf+q
−f=pf+q
n
=
p
n
+
q
n=pn+q
n=pn+q
解得 p = − f + n f − n , q = − 2 n f f − n p=-\frac{f+n}{f-n},q=-\frac{2nf}{f-n} p=−f−nf+n,q=−f−n2nf
代入得到 z ′ z = ( − f + n f − n ) z − 2 n f f − n z'z=(-\frac{f+n}{f-n})z-\frac{2nf}{f-n} z′z=(−f−nf+n)z−f−n2nf
由于此时是在为点 ( x ′ z , y ′ z , z ′ z , w ′ z ) (x'z,y'z,z'z,w'z) (x′z,y′z,z′z,w′z)做变换,所以把 w ′ = 1 w'=1 w′=1写作 w ′ z = z w'z=z w′z=z,得到最终用于透视投影的等式为
x ′ z = ( 2 n r − l ) x − r + l r − l z x'z=(\frac{2n}{r-l})x-\frac{r+l}{r-l}z x′z=(r−l2n)x−r−lr+lz
y ′ z = ( 2 n t − b ) y − t + b t − b z y'z=(\frac{2n}{t-b})y-\frac{t+b}{t-b}z y′z=(t−b2n)y−t−bt+bz
z ′ z = ( − f + n f − n ) z − 2 n f f − n z'z=(-\frac{f+n}{f-n})z-\frac{2nf}{f-n} z′z=(−f−nf+n)z−f−n2nf
w ′ z = z w'z=z w′z=z
写成矩阵的形式为
P p = [ 2 n r − l 0 − r + l r − l 0 0 2 n t − b − t + b t − b 0 0 0 − f + n f − n − 2 n f f − n 0 0 1 0 ] P_p=\begin{bmatrix} \frac{2n}{r-l} & 0 & -\frac{r+l}{r-l} & 0 \\ 0 & \frac{2n}{t-b} & -\frac{t+b}{t-b} & 0 \\ 0 & 0 & -\frac{f+n}{f-n} &-\frac{2nf}{f-n} \\ 0 & 0 & 1 & 0 \end{bmatrix} Pp=⎣⎢⎢⎡r−l2n0000t−b2n00−r−lr+l−t−bt+b−f−nf+n100−f−n2nf0⎦⎥⎥⎤
根据摄像机组件获得以下信息
n
e
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
=
2
⋅
N
e
a
r
⋅
tan
F
O
V
2
nearClipPlaneHeight=2·Near·\tan\frac{FOV}{2}
nearClipPlaneHeight=2⋅Near⋅tan2FOV
f
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
=
2
⋅
F
a
r
⋅
tan
F
O
V
2
farClipPlaneHeight=2·Far·\tan\frac{FOV}{2}
farClipPlaneHeight=2⋅Far⋅tan2FOV
A
s
p
e
c
t
=
n
e
a
r
C
l
i
p
P
l
a
n
e
W
i
d
t
h
n
e
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
Aspect=\frac{nearClipPlaneWidth}{nearClipPlaneHeight}
Aspect=nearClipPlaneHeightnearClipPlaneWidth
A
s
p
e
c
t
=
f
a
r
C
l
i
p
P
l
a
n
e
W
i
d
t
h
f
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
Aspect=\frac{farClipPlaneWidth}{farClipPlaneHeight}
Aspect=farClipPlaneHeightfarClipPlaneWidth
最终求得透视投影矩阵为
M f r u s t u m = [ cot F O V 2 A s p e c t 0 0 0 0 cot F O V 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] M_{frustum}=\begin{bmatrix} \frac{\cot\frac{FOV}{2}}{Aspect} & 0 & 0 & 0 \\ 0 & \cot\frac{FOV}{2} & 0 & 0 \\ 0 & 0 & -\frac{Far+Near}{Far-Near} & -\frac{2·Near·Far}{Far-Near} \\ 0 & 0 & -1 & 0 \end{bmatrix} Mfrustum=⎣⎢⎢⎢⎡Aspectcot2FOV0000cot2FOV0000−Far−NearFar+Near−100−Far−Near2⋅Near⋅Far0⎦⎥⎥⎥⎤
坐标空间的应用