同步自:
https://dingyujie-23.gitee.io/2023/10/22/投影变换与矩阵推导/
前言
本文主要目的是推导投影矩阵。曾经刚了解投影矩阵的时候,面对网上搜到的各种各样的推导非常头疼,因为他们推导前提条件和结果都不一样。本文会在非常明确的前提下推导出投影矩阵,并简略说明Games 101
和《Unity Shader入门精要》
中的投影矩阵的不同之处。
首先明确一点,推导投影矩阵时假设的前提条件不一样时(比如:视锥体位置、左手系或右手系、摄像机看向z轴正方向还是负方向、已知的参数等),推导出的结果会有所不同,。本文的推导过程假设使用右手系,摄像机看向z轴负方向,已知的参数和视锥体位置另有说明。
正交投影
首先假设视锥体近平面
位于z = 0
处,则视锥体远平面
位于z = n-f
处(z < 0),则
z ∈ [ n − f , 0 ] z\in[n - f,0] z∈[n−f,0]
假设近平面的中心位于坐标原点,设视锥体中x和y的取值范围如下:
x
∈
[
−
r
,
r
]
,
r
>
0
x\in[-r, r],r > 0
x∈[−r,r],r>0
y
∈
[
−
t
,
t
]
,
t
>
0
y\in[-t, t],t > 0
y∈[−t,t],t>0
在视锥体内有一点 P ( x p , y p , z p ) P(x_p, y_p, z_p) P(xp,yp,zp)位于视锥体内,如下图所示。
我们的目标是将视锥体内任意一点 P P P的坐标变换至 x , y , z ∈ [ − 1 , 1 ] x,y,z\in[-1 ,1] x,y,z∈[−1,1],变换后的示意图如下:
为了达到这个目的,我们需要将点P的 x , y , z x, y, z x,y,z坐标分别进行缩放:
x
p
′
=
1
r
x
p
x_p' = \frac{1}{r}x_p
xp′=r1xp
y
p
′
=
1
t
y
p
y_p' = \frac{1}{t}y_p
yp′=t1yp
z
p
′
=
2
n
−
f
x
p
−
1
z_p' = \frac{2}{n - f}x_p - 1
zp′=n−f2xp−1
则对应的矩阵为:
[ 1 r 0 0 0 0 1 t 0 0 0 0 2 n − f − 1 0 0 0 1 ] \begin{bmatrix} \frac{1}{r} & 0 & 0 & 0 \\\\ 0 & \frac{1}{t} & 0 & 0 \\\\ 0 & 0 & \frac{2}{n - f} & -1 \\\\ 0 & 0 & 0 & 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡r10000t10000n−f2000−11⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
在Unity中,我们一般使用另外的参数来描述正交相机,即: t = s i z e t = size t=size, a s p e c t = r / t aspect = r / t aspect=r/t,则 r = t ⋅ a s p e c t r = t \cdot aspect r=t⋅aspect,于是投影矩阵可以表示为:
[ 1 a s p e c t ⋅ t 0 0 0 0 1 s i z e 0 0 0 0 2 n − f − 1 0 0 0 1 ] 正交投影矩阵一 \begin{bmatrix} \frac{1}{aspect\cdot t} & 0 & 0 & 0 \\\\ 0 & \frac{1}{size} & 0 & 0 \\\\ 0 & 0 & \frac{2}{n - f} & -1 \\\\ 0 & 0 & 0 & 1 \end{bmatrix} \tag*{正交投影矩阵一} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡aspect⋅t10000size10000n−f2000−11⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤正交投影矩阵一
注意,这个正交投影矩阵与Games 101
和《Unity Shader入门精要》
中的正交投影矩阵都不相同。
与本节的假设相比,《Unity Shader入门精要》第81页
中的正交投影矩阵假设原始视锥体的近平面并没有位于
z
=
0
z=0
z=0处,而是位于
z
=
−
n
z=-n
z=−n处,即对于原始视锥体中的任意一点,
z
∈
[
−
f
,
−
n
]
z\in[-f, -n]
z∈[−f,−n],其正交投影矩阵如下:
[ 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 n − f n + f n − f 0 0 0 1 ] 正交投影矩阵二 \begin{bmatrix} \frac{1}{aspect\cdot size} & 0 & 0 & 0 \\\\ 0 & \frac{1}{size} & 0 & 0 \\\\ 0 & 0 & \frac{2}{n - f} & \frac{n + f}{n - f} \\\\ 0 & 0 & 0 & 1 \end{bmatrix} \tag*{正交投影矩阵二} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡aspect⋅size10000size10000n−f2000n−fn+f1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤正交投影矩阵二
与本节的假设相比,Games 101
中的投影矩阵的推导,假设视锥体位于任何位置(但视锥体始终与坐标轴平行),具体推导可以见:GAMES101-现代计算机图形学入门-闫令琪
透视投影
对于透视投影,其视锥体为一个平截头体(frustum),我们先考虑将其"压缩",即保持近平面不变,将其"压缩"为一个立方体,然后利用透视投影的方法得到结果。
假设平截头体所在四棱锥的顶尖位于坐标原点,朝向z轴负方向,并且关于x轴和y轴对称,有一点 P ( x p , y p , z p ) P(x_p, y_p, z_p) P(xp,yp,zp)位于视锥体内,如下图所示:
首先考虑点 P P P的坐标 x p x_p xp,我们从下往上去看(或者从上往下看,一样的),如下图所示:
则可以很容易的由相似三角形得到变换后的坐标
x p ′ = n − z p x p x_p' = \frac{n}{-z_p}x_p xp′=−zpnxp
同理可得(懒得画图了):
y p ′ = n − z p y p y_p' = \frac{n}{-z_p}y_p yp′=−zpnyp
我们暂时先不处理 z p ′ z_p' zp′。由于式子中含有 − z p -z_p −zp,无法直接写在矩阵当中,所以接下来需要经过一些特别的处理。我们知道,对于齐次坐标:
[ x p y p z p 1 ] 和 [ n x p n y p n z p n ] \begin{bmatrix} x_p\\\\ y_p \\\\ z_p \\\\ 1 \end{bmatrix} 和 \begin{bmatrix} nx_p\\\\ ny_p \\\\ nz_p \\\\ n \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpypzp1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤和⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡nxpnypnzpn⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
这两个齐次坐标表示三维空间中的同一个点。如果我们某一次计算的结果是后一种,则"隐含了"接下来要除以n的操作。因此我们得到如下矩阵:
[ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 − 1 0 ] \begin{bmatrix} n & 0 & 0 & 0 \\\\ 0 & n & 0 & 0 \\\\ ? & ? & ? & ? \\\\ 0 & 0 & -1 & 0 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡n0?00n?000?−100?0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤* [ x p y p z p 1 ] \begin{bmatrix} x_p\\\\ y_p \\\\ z_p \\\\ 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpypzp1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ = [ n x p n y p ? − z p ] \begin{bmatrix} nx_p\\\\ ny_p \\\\ ? \\\\ -z_p \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡nxpnyp?−zp⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
这个矩阵已经可以完美描述 x p x_p xp和 x p x_p xp的变换了,对于 z p z_p zp,我们无法简单的找到公式,但是我们发现对于近平面和远平面内的点,变换后的 z p ′ = z p z_p' = z_p zp′=zp,而位于视锥体内部的点则不一定。并且 z p ′ z_p' zp′与 x p x_p xp和 y p y_p yp无关,于是我们可以根据近平面和远平面上的点列出式子。
对于近平面,其 z p = − n z_p = -n zp=−n:
[ n 0 0 0 0 n 0 0 0 0 A B 0 0 − 1 0 ] \begin{bmatrix} n & 0 & 0 & 0 \\\\ 0 & n & 0 & 0 \\\\ 0 & 0 & A & B \\\\ 0 & 0 & -1 & 0 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡n0000n0000A−100B0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ * [ x p y p − n 1 ] \begin{bmatrix} x_p\\\\ y_p \\\\ -n \\\\ 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpyp−n1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ = [ n x p n y p − n 2 n ] \begin{bmatrix} nx_p\\\\ ny_p \\\\ -n^2 \\\\ n \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡nxpnyp−n2n⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ ,即: [ 0 , 0 , A , B ] [0, 0, A, B] [0,0,A,B] * [ x p y p − n 1 ] \begin{bmatrix} x_p\\\\ y_p \\\\ -n \\\\ 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpyp−n1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ = − n 2 -n^2 −n2
对于远平面,其 z p = − f z_p = -f zp=−f
[ n 0 0 0 0 n 0 0 0 0 A B 0 0 − 1 0 ] \begin{bmatrix} n & 0 & 0 & 0 \\\\ 0 & n & 0 & 0 \\\\ 0 & 0 & A & B \\\\ 0 & 0 & -1 & 0 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡n0000n0000A−100B0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ * [ x p y p − f 1 ] \begin{bmatrix} x_p\\\\ y_p \\\\ -f \\\\ 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpyp−f1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ = [ n x p n y p − f 2 f ] \begin{bmatrix} nx_p\\\\ ny_p \\\\ -f^2 \\\\ f \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡nxpnyp−f2f⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ ,即: [ 0 , 0 , A , B ] [0, 0, A, B] [0,0,A,B] * [ x p y p − f 1 ] \begin{bmatrix} x_p\\\\ y_p \\\\ -f \\\\ 1 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡xpyp−f1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ = − f 2 -f^2 −f2
解这个方程组即可得到:
A = n + f A=n+f A=n+f
B = n f B=nf B=nf
B
B
B的值与Games 101
中推导出的值
(
B
=
−
n
f
)
(B=-nf)
(B=−nf)不同(大概是因为他们假设
z
∈
[
n
,
f
]
z\in[n, f]
z∈[n,f],与我们的假设不同)。
于是可得矩阵
[ n 0 0 0 0 n 0 0 0 0 n + f n f 0 0 − 1 0 ] 透视投影矩阵一 \begin{bmatrix} n & 0 & 0 & 0 \\\\ 0 & n & 0 & 0 \\\\ 0 & 0 & n+f & nf \\\\ 0 & 0 & -1 & 0 \end{bmatrix} \tag*{透视投影矩阵一} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡n0000n0000n+f−100nf0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤透视投影矩阵一
经过透视投影矩阵一
投影后,原视锥体由平截头体变为一个立方体,坐标取值范围为
x
∈
[
−
r
,
r
]
x\in[-r, r]
x∈[−r,r],
y
∈
[
−
t
,
t
]
y\in[-t, t]
y∈[−t,t],
z
∈
[
−
f
,
−
n
]
z\in[-f, -n]
z∈[−f,−n],再次对其应用正交投影矩阵二
:
[
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
n
−
f
n
+
f
n
−
f
0
0
0
1
]
\begin{bmatrix} \frac{1}{aspect\cdot size} & 0 & 0 & 0 \\\\ 0 & \frac{1}{size} & 0 & 0 \\\\ 0 & 0 & \frac{2}{n - f} & \frac{n + f}{n - f} \\\\ 0 & 0 & 0 & 1 \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡aspect⋅size10000size10000n−f2000n−fn+f1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ *
[
n
0
0
0
0
n
0
0
0
0
n
+
f
n
f
0
0
−
1
0
]
\begin{bmatrix} n & 0 & 0 & 0 \\\\ 0 & n & 0 & 0 \\\\ 0 & 0 & n+f & nf \\\\ 0 & 0 & -1 & 0 \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡n0000n0000n+f−100nf0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤ =
[
n
a
s
p
e
c
t
⋅
s
i
z
e
0
0
0
0
n
s
i
z
e
0
0
0
0
n
+
f
n
−
f
2
n
f
n
−
f
0
0
−
1
0
]
透视投影矩阵二
\begin{bmatrix} \frac{n}{aspect\cdot size} & 0 & 0 & 0 \\\\ 0 & \frac{n}{size} & 0 & 0 \\\\ 0 & 0 & \frac{n+f}{n - f} & \frac{2nf}{n-f} \\\\ 0 & 0 & -1 & 0 \end{bmatrix} \tag*{透视投影矩阵二}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡aspect⋅sizen0000sizen0000n−fn+f−100n−f2nf0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤透视投影矩阵二
接下来是换参数的问题。在Unity中,透视相机由 a s p e c t aspect aspect、 f o v fov fov、 n e a r ( n ) near(n) near(n)和 f a r ( f ) far(f) far(f)定义
cot f o v 2 = n s i z e \cot\frac{fov}{2} = \frac{n}{size} cot2fov=sizen
所以最终的透视投影矩阵可以写为
[ cot f o v 2 a s p e c t 0 0 0 0 cot f o v 2 0 0 0 0 n + f n − f 2 n f n − f 0 0 − 1 0 ] 透视投影矩阵三 \begin{bmatrix} \frac{\cot\frac{fov}{2}}{aspect} & 0 & 0 & 0 \\\\ 0 & \cot\frac{fov}{2} & 0 & 0 \\\\ 0 & 0 & \frac{n+f}{n - f} & \frac{2nf}{n-f} \\\\ 0 & 0 & -1 & 0 \end{bmatrix} \tag*{透视投影矩阵三} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡aspectcot2fov0000cot2fov0000n−fn+f−100n−f2nf0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤透视投影矩阵三
透视投影矩阵三
与《Unity Shader入门精要》第79页
的矩阵相同。
对本文有任何疑问,欢迎指出。