1. 数学基础
1.1. 3D坐标系
名称 | 坐标系 |
---|---|
DirectX | 左手坐标系 |
OpenGL | 右手坐标系 |
1.2. 点
3D 坐标系中的点由三个坐标值组成,分别表示该点的 x、y、z 坐标。如(1, 2, 3)表示该点在 x 轴方向上 1 个单位,在 y 轴方向上 2 个单位,在 z 轴方向上 3 个单位。通常用vec3来表示一个点,如vec3(1, 2, 3)。 实际使用中,我们通常使用齐次坐标,即vec4来表示一个点,其中第四个分量通常为1.0f,表示该点位于三维空间中。如vec4(1, 2, 3, 1.0f)。
1.3. 矩阵
矩阵是矩形的值阵列,在3D 图形计算中要用到的矩阵通常为 4*4 的矩阵。矩阵可以用来表示平移、旋转、缩放等变换。矩阵的乘法满足结合律,即 (AB)C = A(BC)。
下图是一个 4*4 的矩阵的示例:
[
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
]
\begin{bmatrix} a & b & c & d \\ e & f & g & h \\ i & j & k & l \\ m & n & o & p \\ \end{bmatrix}
aeimbfjncgkodhlp
我们用mat4来表示一个 4*4 的矩阵
mat4::identity()
单位矩阵,即对角线上的元素为1,其他元素为0的矩阵
[
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}
1000010000100001
任何矩阵与单位矩阵相乘,结果仍为该矩阵
转置矩阵
以主对角线为轴,将矩阵进行翻转,得到的矩阵称为转置矩阵
比如下面的矩阵:
[
a
e
i
m
b
f
j
n
c
g
k
o
d
h
l
p
]
\begin{bmatrix} a & e & i & m \\ b & f & j & n \\ c & g & k & o \\ d & h & l & p \\ \end{bmatrix}
abcdefghijklmnop
转置矩阵为:
[
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
]
\begin{bmatrix} a & b & c & d \\ e & f & g & h \\ i & j & k & l \\ m & n & o & p \\ \end{bmatrix}
aeimbfjncgkodhlp
注意右上角的T,表示转置矩阵。
GLM 库中提供了glm::transpose()函数,GLSL中提供了transpose()函数,用于计算矩阵的转置矩阵。
矩阵加法
矩阵加法满足交换律,即 A+B=B+A。
GLM 库中提供了glm::add()函数,GLSL中重载了+运算符,用于计算矩阵的加法。
矩阵乘法
矩阵乘法不满足交换律,即 AB≠BA。
3D图形学中,点与矩相乘通常从右向左,得到的是该点在矩阵变换后的新坐标,即得到的仍然是**点
**。
此处的点采用了齐次坐标,即第四个分量通常为1.0f。
矩阵与矩阵相乘,得到的是**矩阵
**。
GLM 、GLSL库中重载了*运算符,用于计算矩阵的乘法。
在3D图形学中,将大量使用 44矩阵 与44矩阵相乘,以进行矩阵的合并,从而得到一个变换矩阵。
逆矩阵
逆矩阵是矩阵的乘法逆运算,即 AB=BA=I,其中 I 为单位矩阵。
A
的逆矩阵记为
A
−
1
A的逆矩阵记为A^{-1}
A的逆矩阵记为A−1
A
−
1
的逆矩阵记为
(
A
−
1
)
−
1
=
A
A^{-1}的逆矩阵记为(A^{-1})^{-1}=A
A−1的逆矩阵记为(A−1)−1=A
2.1. 变换矩阵
2.1.1. 平移矩阵
平移矩阵用于将点在 x、y、z 轴方向上平移一定的距离。
注意是从右向左相乘,得到的是该点在矩阵变换后的新坐标,即得到的仍然是**点
**。
在GLM中,平移矩阵使用glm::translate()函数来创建。
2.1.2. 缩放矩阵
缩放矩阵用于将点在 x、y、z 轴方向上缩放一定的倍数。
在GLM中,缩放矩阵使用glm::scale()函数来创建。
可以通过缩放矩阵来将坐标系从右手坐标系转换为左手坐标系
其缩放矩阵为:
[
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}
1000010000−100001
2.1.3. 旋转矩阵
旋转矩阵用于将点绕 x、y、z 轴旋转一定的角度。
在GLM中,旋转矩阵使用glm::rotate()函数来创建。
实际使用中,当3D空间中旋转轴没有穿过原点时,需要按以下方式进行:
- 先平移至原点
- 再进行旋转,即绕X,Y,Z轴旋转 。这就是欧拉角旋转,但这种方式在某些情况下会存在万向节锁的问题。所以建议使用四元数旋转。
- 最后再平移回原点。
2.2. 向量
向量是具有大小和方向的量,通常用箭头表示
向量
V
⃗
\vec{V}
V的两种表示方式 ,我们通常将向量的起点放在原点,即起点为(0,0,0),向量的终点为(x,y,z)。
V ⃗ = [ x y z ] \vec{V} = \begin{bmatrix} x \\ y \\ z \\ \end{bmatrix} V= xyz
向量的加减法
归一化
向量的模(长度)为:
x
2
+
y
2
+
z
2
\sqrt{x^2+y^2+z^2}
x2+y2+z2
归一化就是将向量的模(长度)变为1,即:
V
⃗
x
2
+
y
2
+
z
2
\frac{\vec{V}}{\sqrt{x^2+y^2+z^2}}
x2+y2+z2V
在GLM中,使用glm::normalize()函数来对向量进行归一化。
向量的点积
点积是向量的一个重要运算,其结果是一个标量
。
A
⃗
⋅
B
⃗
=
A
⃗
×
B
⃗
=
x
1
x
2
+
y
1
y
2
+
z
1
z
2
\vec{A} \cdot \vec{B} = \vec{A} \times \vec{B} = x_1x_2+y_1y_2+z_1z_2
A⋅B=A×B=x1x2+y1y2+z1z2
在GLM中,使用glm::dot()函数来计算两个向量的点积。
采用向量的点积可以计算两个向量的夹角,即:
cos
θ
=
A
⃗
⋅
B
⃗
x
a
2
+
y
a
2
+
z
a
2
×
x
b
2
+
y
b
2
+
z
b
2
\cos{\theta} = \frac{\vec{A} \cdot \vec{B}}{\sqrt{xa^2+ya^2+za^2} \times \sqrt{xb^2+yb^2+zb^2}}
cosθ=xa2+ya2+za2×xb2+yb2+zb2A⋅B
当两个向量均为单位向量时,点积等于两个向量的夹角的cos值:
A
⃗
⋅
B
⃗
=
cos
θ
\vec{A} \cdot \vec{B} = \cos{\theta}
A⋅B=cosθ
实际使用中,我们通常将向量归一化
归一化向量点积的特点
- 点积等于两个向量夹角的cos值
- 如果向量正交,则点积等于0
- 如果向量平行且方向一致,则点积等于1
- 如果向量平行且方向相反,则点积等于-1
- 如果两个向量的夹角在-90度到90度之间,则点积为正数
向量的叉积
叉积是向量的另一个重要运算,其结果是一个向量
。
A
⃗
×
B
⃗
=
[
y
1
z
2
−
y
2
z
1
z
1
x
2
−
z
2
x
1
x
1
y
2
−
x
2
y
1
]
\vec{A} \times \vec{B} = \begin{bmatrix} y_1z_2-y_2z_1 \\ z_1x_2-z_2x_1 \\ x_1y_2-x_2y_1 \\ \end{bmatrix}
A×B=
y1z2−y2z1z1x2−z2x1x1y2−x2y1
在GLM中,使用glm::cross()函数来计算两个向量的叉积。
叉积具有以下特点:
- 叉积的方向垂直于两个向量所在的平面
- 叉积生成的新向量与两个向量形成的坐标系是右手坐标系
- 叉积不满足交换律,即cross(A,B) != cross(B,A),坐标系不同,一个是右手坐标系,一个是左手坐标系
4.1. 局部坐标系和世界坐标系
局部坐标系:物体在局部坐标系中的位置和方向,也称为模型坐标系。
世界坐标系:世界坐标系是全局坐标系,是全局统一的坐标系
将物体从局部坐标系变换到世界坐标系的过程称为模型变换
,模型变换包括平移、旋转和缩放。其使用的矩阵称为模型矩阵
或M。
4.2. 视觉空间
视觉空间是观察者所看到的空间,也称为视口空间
或视口坐标系
。视觉空间由观察者位置、观察方向和观察目标组成。
如上图所示,观察3D世界:
- 观察者位置:观察者所在的位置,即相机位置
- 观察方向:观察者所面向的方向,即相机方向,生成相机自身的坐标系UVN ,也称为相机坐标系或视觉空间
- 观察目标:观察者所观察的目标,即相机目标
- 定义一个视体
- 将视体内的对象投影到投影平面上
在OpenGL中,有一个固定在原点,面向z轴负方向的相机,即相机位置为(0,0,0),观察方向为(0,0,-1)
局部空间 → 世界空间 → 视觉空间(相机空间)→ 裁剪空间(投影空间) → 屏幕空间
从世界空间到视觉空间,需要使用视图矩阵V进行变化 ,其逻辑如下:
- 将P平移,其向量为负相机位置
- 将P旋转,其角度为负的相机旋转的欧拉角
注:相机不需要缩放,也就没有缩放矩阵
上面的变换称为视图变换
,其使用的矩阵称为视图矩阵
或V。
注意:视图变换是针对世界坐标系进行的,而不是针对局部坐标系进行的。
由模型矩阵M、视图矩阵生成 模型-视图(MV)矩阵:
M
V
=
V
×
M
MV = V \times M
MV=V×M
4.3. 投影空间
投影空间是投影平面上所显示的空间,也称为裁剪空间
或裁剪坐标系
。投影空间由观察者位置、观察方向和观察目标组成。
4.3.1. 正交投影
正交投影是投影空间的一种,其投影平面与坐标轴平行,也称为平行投影
。正交投影的投影平面称为视平面
,其投影结果称为正交投影
。
在OpenGL中,使用glOrtho()函数来创建正交投影矩阵。
正交投影的特点:
- 远近裁剪面相等,远的物体和近的物体一样大
- 与我们眼睛所见的真实世界不同
我们在3D图形学中,通常使用透视投影,而不是正交投影。
4.3.2. 透视投影
透视投影是投影空间的一种,其投影平面与坐标轴不平行,近的物体显示得大,远的物体显示得小。通常使用透视矩阵P,需要的参数有:
- 宽高比
- 近裁剪面
- 远裁剪面
- Y方向视场角(FOV)
采用glm::perspective()函数来创建透视投影矩阵(4*4矩阵)。
4.4. LookAt矩阵
LookAt矩阵是视图矩阵的一种,用于定义观察者(相机)在3D空间中的视角。它将世界空间中的坐标转换到观察空间(相机空间)中。
构成要素
LookAt矩阵需要三个关键向量:
- 相机位置(Eye Position):观察者在世界空间中的位置坐标
- 观察目标(Target Position):相机看向的目标点
- 上方向向量(Up Vector):定义相机的上方向,通常是(0,1,0)
在OpenGL中,使用glm::lookAt()函数来创建LookAt矩阵。