使用书籍《Unity Shader 入门精要》,冯乐乐著。
1. 笛卡尔坐标系
部分术语如下
- 基矢量(basis vector),三维坐标系的三个轴
- 标准正交基(orthonormal basis),三维坐标系的三个相互垂直,且长度为 1,这样的基矢量被称为标准正交基
- 正交基(orthogonal basis),在一些坐标系中坐标轴之间相互垂直但长度不为 1
2. 点和矢量
- 单位矢量与归一化
使矢量的模为 1。零矢量是不能被归一化的 - 矢量的点积(dot product)
公式一:
a
?
b
=
(
a
x
,
a
y
,
a
z
)
?
(
b
x
,
b
y
,
b
z
)
=
a
x
b
x
+
a
y
b
y
+
a
z
b
z
\bf a?b=\it (a_x,a_y,a_z )?(b_x,b_y,b_z )=a_x b_x+a_y b_y+a_z b_z
a?b=(ax?,ay?,az?)?(bx?,by?,bz?)=ax?bx?+ay?by?+az?bz? 公式二:
a
?
b
=
∣
a
∣
∣
b
∣
c
o
s
?
θ
\bf a?b=|a||b|\it cos?θ
a?b=∣a∣∣b∣cos?θ - 矢量的叉积(cross product)
公式一:
a
×
b
=
(
a
x
,
a
y
,
a
z
)
×
(
b
x
,
b
y
,
b
z
)
=
(
a
y
b
z
?
a
z
b
y
,
?
a
z
b
x
?
a
x
b
z
,
?
a
x
b
y
?
a
y
b
x
)
\bf a×b=\it (a_x,a_y,a_z )×(b_x,b_y,b_z )=(a_y b_z?a_z b_y,~a_z b_x?a_x b_z,~a_x b_y?a_y b_x)
a×b=(ax?,ay?,az?)×(bx?,by?,bz?)=(ay?bz??az?by?,?az?bx??ax?bz?,?ax?by??ay?bx?) 公式二:
∣
a
×
b
∣
=
∣
a
∣
∣
b
∣
s
i
n
?
θ
\bf |a×b|=|a||b|\it sin?θ
∣a×b∣=∣a∣∣b∣sin?θ 关于叉积的方向:使用右手,手掌的四指指向方向 a,然后向方向 b 弯曲。此时大拇指指向叉积方向。
3. 矩阵
M
=
[
m
11
m
12
m
13
m
21
m
22
m
23
m
31
m
32
m
33
]
\bf M=\begin{bmatrix} {m_{11}}&{m_{12}}&{m_{13}}\\ {m_{21}}&{m_{22}}&{m_{23}}\\ {m_{31}}&{m_{32}}&{m_{33}}\\ \end{bmatrix}
M=???m11?m21?m31??m12?m22?m32??m13?m23?m33?????
- 方块矩阵(square matrix)
行列元素数量相同的矩阵 - 对角矩阵(diagonal matrix)
除了对角线上的元素,其余所有元素都是 0 的矩阵 - 单位矩阵(identity matrix)
一种对角矩阵,对角线上的元素都为 1.用
I
n
\rm I_n
In? 表示 - 转置矩阵(transposed matrix)
对原矩阵
M
\rm M
M 进行转置(就是翻转,行列互换),得到
M
T
\rm M^{\it T}
MT - 逆矩阵(inverse matrix)
原矩阵
M
\rm M
M 的转置用
M
?
1
\rm M^{\it -1}
M?1 表示。逆矩阵满足
M
M
?
1
=
M
?
1
M
=
I
\rm MM^{\it -1}=\rm M^{\it -1}M=I
MM?1=M?1M=I
如果一个矩阵有对应的逆矩阵,就说这个矩阵是 可逆的(invertible) 或 非奇异的(nonsingular),否则说是 不可逆的(noninvertible) 或 奇异的(singular)
- 正交矩阵(orthogonal matrix)
如果一个矩阵
M
\rm M
M 和它的转置
M
?
1
\rm M^{\it -1}
M?1 的乘积是单位矩阵
I
\rm I
I 的话(
M
M
?
1
=
I
\rm MM^{\it -1}=I
MM?1=I),就说这个矩阵是正交的。 即,
M
T
=
M
?
1
\rm M^{\it T}=\rm M^{\it -1}
MT=M?1
3.1. 矩阵运算
-
矩阵与矩阵的乘法 A 和 B 分别为两个矩阵。若乘式 AB 成立,AB 表达的含义是:A 的第 i 行所有元素,与 B 的第 j 列的所有元素,分别相乘,并相加,构成 AB 结果矩阵的第 i 行第 j 列的元素。 -
unity 中的矩阵运算惯例 使用列矩阵,从右往左运算 如
C
B
A
v
\rm CBAv
CBAv,相当于
(
C
(
C
(
A
v
)
)
)
\rm (C(C(Av)))
(C(C(Av))); 又如
v
A
T
B
T
C
T
\rm vA^{\it T}B^{\it T}C^{\it T}
vATBTCT,相当于
(
(
(
v
A
T
)
B
T
)
C
T
)
\rm (((vA^{\it T})B^{\it T})C^{\it T})
(((vAT)BT)CT)
3.2. 齐次坐标
使用 N+1 个数来表示 N 维坐标。
一个三维坐标,其
w
w
w 分量设为 1;一个三维矢量,其
w
w
w 分量设为 0。
3.3. 矩阵变换
使用 4×4 的矩阵来表示平移、旋转和缩放。 一个基础变换矩阵可以分成 4 个部分:
[
M
3
×
3
t
3
×
1
0
1
×
3
1
]
\begin{bmatrix} {\bf M_{\rm 3×3}}&{\bf t_{\rm 3×1}}\\ {\bf 0_{\rm 1×3}}&{1}\\ \end{bmatrix}
[M3×3?01×3??t3×1?1?]
其中,
M
3
×
3
\bf M_{\rm 3×3}
M3×3? 表示缩放和旋转,
t
3
×
1
\bf t_{\rm 3×1}
t3×1? 表示平移,
0
1
×
3
\bf 0_{\rm 1×3}
01×3? 表示零矩阵,
1
1
1 就是标量 1。
3.3.1. 平移矩阵
平移一个位置:
[
1
0
0
t
x
0
1
0
t
y
0
0
1
t
z
0
0
0
1
]
[
x
y
z
1
]
=
[
x
+
t
x
y
+
t
y
z
+
t
z
1
]
\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} x\\y\\z\\1 \end{bmatrix}= \begin{bmatrix} x+t_x\\y+t_y\\z+t_z\\1 \end{bmatrix}
?????1000?0100?0010?tx?ty?tz?1???????????xyz1??????=?????x+tx?y+ty?z+tz?1??????
平移一个矢量,不会对矢量产生任何影响:
[
1
0
0
t
x
0
1
0
t
y
0
0
1
t
z
0
0
0
1
]
[
x
y
z
0
]
=
[
x
y
z
0
]
\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} x\\y\\z\\0 \end{bmatrix}= \begin{bmatrix} x\\y\\z\\0 \end{bmatrix}
?????1000?0100?0010?tx?ty?tz?1???????????xyz0??????=?????xyz0??????
- 平移矩阵的逆矩阵就是反向平移的矩阵
- 平移矩阵不是正交矩阵
3.3.2. 缩放矩阵
缩放一个位置:
[
k
x
0
0
0
0
k
y
0
0
0
0
k
z
0
0
0
0
1
]
[
x
y
z
1
]
=
[
k
x
x
k
y
y
k
z
z
1
]
\begin{bmatrix} {k_x}&{0}&{0}&{0}\\ {0}&{k_y}&{0}&{0}\\ {0}&{0}&{k_z}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\1 \end{bmatrix}= \begin{bmatrix} k_xx\\k_yy\\k_zz\\1 \end{bmatrix}
?????kx?000?0ky?00?00kz?0?0001???????????xyz1??????=?????kx?xky?ykz?z1??????
缩放一个矢量:
[
k
x
0
0
0
0
k
y
0
0
0
0
k
z
0
0
0
0
1
]
[
x
y
z
0
]
=
[
k
x
x
k
y
y
k
z
z
0
]
\begin{bmatrix} {k_x}&{0}&{0}&{0}\\ {0}&{k_y}&{0}&{0}\\ {0}&{0}&{k_z}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\0 \end{bmatrix}= \begin{bmatrix} k_xx\\k_yy\\k_zz\\0 \end{bmatrix}
?????kx?000?0ky?00?00kz?0?0001???????????xyz0??????=?????kx?xky?ykz?z0??????
- 若
k
x
=
k
y
=
k
z
k_x=k_y=k_z
kx?=ky?=kz?,这样的缩放称为统一缩放(uniform sccale),否则称为非统一缩放(nonuniform scale)
- 缩放矩阵的逆矩阵意为缩放到原系数的倒数
- 这样只能沿着坐标轴方向进行缩放。若希望在任意方向上进行缩放,需要使用复合变换
3.3.3. 旋转矩阵
令点绕着
x
x
x 轴旋转
θ
θ
θ 度:
R
x
(
θ
)
=
[
1
0
0
0
0
c
o
s
θ
?
s
i
n
θ
0
0
s
i
n
θ
c
o
s
θ
0
0
0
0
1
]
\bf R_{\it x}(θ)=\begin{bmatrix} {1}&{0}&{0}&{0}\\ {0}&{\rm cos\it θ}&{\rm -sin\it θ}&{0}\\ {0}&{\rm sin\it θ}&{\rm cos\it θ}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}
Rx?(θ)=?????1000?0cosθsinθ0?0?sinθcosθ0?0001??????
令点绕着
y
y
y 轴旋转
θ
θ
θ 度:
R
y
(
θ
)
=
[
c
o
s
θ
0
s
i
n
θ
0
0
1
0
0
?
s
i
n
θ
0
c
o
s
θ
0
0
0
0
1
]
\bf R_{\it y}(θ)=\begin{bmatrix} {\rm cos\it θ}&{0}&{\rm sin\it θ}&{0}\\ {0}&{1}&{0}&{0}\\ {\rm -sin\it θ}&{0}&{\rm cos\it θ}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}
Ry?(θ)=?????cosθ0?sinθ0?0100?sinθ0cosθ0?0001??????
令点绕着
z
z
z 轴旋转
θ
θ
θ 度:
R
z
(
θ
)
=
[
c
o
s
θ
?
s
i
n
θ
0
0
s
i
n
θ
c
o
s
θ
0
0
0
0
1
0
0
0
0
1
]
\bf R_{\it z}(θ)=\begin{bmatrix} {\rm cos\it θ}&{\rm -sin\it θ}&{0}&{0}\\ {\rm sin\it θ}&{\rm cos\it θ}&{0}&{0}\\ {0}&{0}&{1}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}
Rz?(θ)=?????cosθsinθ00??sinθcosθ00?0010?0001??????
- 旋转矩阵的逆矩阵意为旋转相反角度
- 旋转矩阵是正交矩阵,并且多个旋转矩阵串联也是正交的
3.3.4. 复合变换
可以把平移、旋转和缩放进行组合。式子如下,注意列矩阵的从右到左顺序。
p
n
e
w
=
M
t
r
a
n
s
l
a
t
i
o
n
M
r
o
t
a
t
i
o
n
M
s
c
a
l
e
p
o
l
d
\bf p_{\it new}=\bf M_{\it translation}M_{\it rotation}M_{\it scale}p_{\it old}
pnew?=Mtranslation?Mrotation?Mscale?pold?
- 约定的变换顺序是 “缩放、旋转、平移”。
- 对于旋转的变换顺序,unity 中顺序是 “z轴、x轴、y轴”——在此时请在矩阵乘法中按 “yxz” 的顺序进行,因为旋转会同时转动轴
4. 坐标变换
使用矩阵进行坐标变换。
进行变换的矩阵被称为过渡矩阵。因为坐标是用一维列向量表示的,因此过度矩阵应左乘上去,得到转换结果。
若想从坐标空间 C 转换为坐标空间 P,需要先用坐标空间 P 表示坐标空间 C 下的 x、y、z 轴,用三个三维列向量
x
c
\bf x_{\it c}
xc?、
y
c
\bf y_{\it c}
yc?、
z
c
\bf z_{\it c}
zc? 表示,以及原点的相对位置
O
c
\bf O_{\it c}
Oc?。 对于一个坐标空间 C 下的坐标
A
c
=
[
a
b
c
]
T
\bf A_{\it c}=\begin{bmatrix}a&b&c\end{bmatrix}^{\it T}
Ac?=[a?b?c?]T,在坐标空间 P 下的坐标为:
A
p
=
O
c
+
a
x
c
+
b
y
c
+
c
z
c
\bf A_{\it p}=O_{\it c}+\it a\bf x_{\it c}+\it b\bf y_{\it c}+\it c\bf z_{\it c}
Ap?=Oc?+axc?+byc?+czc? 然后使用矩阵进行表示——
转换一个位置:
A
p
=
[
x
c
y
c
z
c
O
c
]
[
a
b
c
1
]
T
\bf A_{\it p}= \begin{bmatrix}\bf x_{\it c}&\bf y_{\it c}&\bf z_{\it c}&\bf O_{\it c}\end{bmatrix} \begin{bmatrix}a&b&c&1\end{bmatrix}^{\it T}
Ap?=[xc??yc??zc??Oc??][a?b?c?1?]T
转换一个矢量:
A
p
=
[
x
c
y
c
z
c
]
[
a
b
c
]
T
\bf A_{\it p}= \begin{bmatrix}\bf x_{\it c}&\bf y_{\it c}&\bf z_{\it c}\end{bmatrix} \begin{bmatrix}a&b&c\end{bmatrix}^{\it T}
Ap?=[xc??yc??zc??][a?b?c?]T
4.1. 观察空间(view space)
也称为摄像机空间(camera space)。它不是最终的屏幕空间,而是一个三维空间。
这里有个坐标变换的特殊地方。
与其他空间不同,摄像机的观察空间的正前方指向的是 -z 轴方向,是反过来的。若需要对过度矩阵进行 z 轴取反,可以左乘以下这个矩阵:
[
1
0
0
0
0
1
0
0
0
0
?
1
0
0
0
0
1
]
\begin{bmatrix} {1}&{0}&{0}&{0}\\ {0}&{1}&{0}&{0}\\ {0}&{0}&{-1}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}
?????1000?0100?00?10?0001??????
4.2. 裁剪空间(clip space)
使用裁剪矩阵(clip matrix),将顶点从观察空间转换到裁剪空间(也称为齐次裁剪空间)。
使用视锥体(view frustum) 决定裁剪空间。视锥体由六个裁剪平面(clip planes) 包围而成。视锥体有两种类型:正交投影、透视投影。 处于这块空间内部的图元会被保留,完全位于这块空间外部的图元将会被剔除,在空间边界相交的部分会被裁剪。
4.2.1. 透视投影(perspective projection)
裁剪空间由 6 个裁剪平面组成。其中有两个特殊的裁剪平面:近裁剪平面(near clip plane)、远裁剪平面(far clip plane)。它们共同决定了摄像机可以看到的深度范围。
Unity 中的 Camera 组件,通过 Field of View (简称 FOV)属性改变视锥体的张开角度;在 Clipping Planes 中的 Near Far 属性可以分别设置近裁剪平面和远裁剪平面距离摄像机的距离。
可以通过以下公式得到两个裁剪平面的高度(宽度用纵横比得到):
n
e
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
=
2
?
N
e
a
r
?
t
a
n
F
O
V
2
nearClipPlaneHeight=2·Near·\rm tan \it \frac {FOV}{\rm 2}
nearClipPlaneHeight=2?Near?tan2FOV?
n
e
a
r
C
l
i
p
P
l
a
n
e
H
e
i
g
h
t
=
2
?
N
e
a
r
?
t
a
n
F
O
V
2
nearClipPlaneHeight=2·Near·\rm tan \it \frac {FOV}{\rm 2}
nearClipPlaneHeight=2?Near?tan2FOV?
用 Aspect 表示摄像机的纵横比,透视投影的投影矩阵如下:
M
f
r
u
s
t
u
m
=
[
c
o
t
F
O
V
2
A
s
p
e
c
t
0
0
0
0
c
o
t
F
O
V
2
0
0
0
0
?
F
a
r
+
N
e
a
r
F
a
r
?
N
e
a
r
?
2
?
F
a
r
?
N
e
a
r
F
a
r
?
N
e
a
r
0
0
?
1
0
]
\bf M_{\it frustum}=\begin{bmatrix} {\rm \frac {cot \frac{FOV}{2}}{Aspect}}&{0}&{0}&{0}\\ {0}&{\rm cot \frac {FOV}{2}}&{0}&{0}\\ {0}&{0}&{\rm -\frac {Far+Near}{Far-Near}}&{\rm -\frac {2·Far·Near}{Far-Near}}\\ {0}&{0}&{-1}&{0}\\ \end{bmatrix}
Mfrustum?=??????Aspectcot2FOV??000?0cot2FOV?00?00?Far?NearFar+Near??1?00?Far?Near2?Far?Near?0???????
把顶点乘上投影矩阵,就会这样:
M
f
r
u
s
t
u
m
[
x
y
z
1
]
=
[
x
c
o
t
F
O
V
2
A
s
p
e
c
t
y
c
o
t
F
O
V
2
?
z
F
a
r
+
N
e
a
r
F
a
r
?
N
e
a
r
?
2
?
F
a
r
?
N
e
a
r
F
a
r
?
N
e
a
r
?
z
]
\bf M_{\it frustum} \begin{bmatrix} x\\y\\z\\1 \end{bmatrix}= \begin{bmatrix} {x\frac {cot \frac{FOV}{2}}{Aspect}}\\ {y\rm cot \frac {FOV}{2}}\\ {-z\rm \frac {Far+Near}{Far-Near}-\rm \frac {2·Far·Near}{Far-Near}}&{}\\ {-z}\\ \end{bmatrix}
Mfrustum??????xyz1??????=??????xAspectcot2FOV??ycot2FOV??zFar?NearFar+Near??Far?Near2?Far?Near??z????????
可见,经过投影的顶点,
w
w
w 分量变为了取反后的 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
4.2.2. 正交投影(orthographic projection)
用 unity 的 camera 组件中的 Size 属性改变锥体竖直方向上高度的一半,正交投影的投影矩阵如下:
M
o
r
t
h
o
=
[
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
]
\bf M_{\it ortho}=\begin{bmatrix} {\rm \frac {1}{Aspect·Size}}&{0}&{0}&{0}\\ {0}&{\rm \frac {1}{Size}}&{0}&{0}\\ {0}&{0}&{\rm -\frac {2}{Far-Near}}&{\rm -\frac {Far+Near}{Far-Near}}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}
Mortho?=?????Aspect?Size1?000?0Size1?00?00?Far?Near2?0?00?Far?NearFar+Near?1??????
使用正交投影的投影矩阵对顶点进行变换后,坐标的
w
w
w 分量仍然为 1。
4.3. 屏幕空间(screen space)
经过投影矩阵的变换、裁剪的操作后,就需要把视锥体投影到屏幕空间了。
4.3.1. 齐次除法(homogeneous division)
又称为透视除法(perspective division),做法是用齐次坐标系的
w
w
w 分量去除 x、y、z 分量,得到的坐标在 OpenGL 中被称作归一化的设备坐标(Normalized Device Coordinates, NDC)。对正交投影来说,它的裁剪空间已经是一个立方体了,齐次除法并没有实际影响。
5. 法线变换
法线(normal),又称为法矢量(normal vector)。一种方向矢量,例如用来计算光照。
需要注意转换方法。若是使用与顶点同一个变换矩阵,可能无法维持法线的垂直性。
切线(tangent),又称为切矢量(tangent vector)。
|