齐次坐标系
齐次坐标系是为了区分空间点和向量的。三维空间中, ( x , y , z ) (x,y,z) (x,y,z)可以表示一个点 p p p的位置,但是也可以表示一个向量 v \bf{v} v。对于点的移动是有实际意义的,但是移动向量没有任何意义!点和向量在三维空间中的真正区别在于是否支持移动。
引入齐次坐标 ( x , y , z , w ) T (x,y,z,w)^T (x,y,z,w)T, w = 1 w=1 w=1表示空间的点, w = 0 w=0 w=0表示空间的向量。这样是为了后期矩阵变换的时候,统一运算规则,不用单独区分点或者向量。
空间变换矩阵
一般来说,在图形学中,使用矩阵的方式进行变换,可以把多个连续的操作压缩到一个中,减少计算量;同时利用矩阵的优化算法,可以提高计算效率。
位移矩阵:
O
f
f
s
e
t
=
[
1
0
0
T
x
0
1
0
T
y
0
0
1
T
z
0
0
0
1
]
\bf{Offset}= \begin{bmatrix} 1 & 0 & 0 & \bf{T_x} \\ 0 & 1 & 0 & \bf{T_y}\\ 0 & 0 & 1 & \bf{T_z} \\ 0 & 0 & 0 & 1 \end{bmatrix}
Offset=⎣⎢⎢⎡100001000010TxTyTz1⎦⎥⎥⎤
T
x
\bf{T_x}
Tx、
T
y
\bf{T_y}
Ty和
T
z
\bf{T_z}
Tz分别表示沿着
x
x
x、
y
y
y和
z
z
z轴的平移距离。根据矩阵的运算法则,如果是点的坐标,那么
w
=
1
w=1
w=1正好线性累加上位移;如果是向量,
w
=
0
w=0
w=0会抵消位移的效果。
绕
x
x
x轴的旋转矩阵:
R
o
t
a
t
i
o
n
X
=
[
1
0
0
0
0
cos
θ
−
sin
θ
0
0
sin
θ
cos
θ
0
0
0
0
1
]
\bf{RotationX}= \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos{\theta} & -\sin{\theta} & 0\\ 0 & \sin{\theta} & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
RotationX=⎣⎢⎢⎡10000cosθsinθ00−sinθcosθ00001⎦⎥⎥⎤
这是右手系的变换矩阵,
θ
\theta
θ是从
x
x
x轴负向看向正向顺时针的旋转角度。
绕
y
y
y轴的旋转矩阵:
R
o
t
a
t
i
o
n
Y
=
[
cos
θ
0
sin
θ
0
0
1
0
0
−
sin
θ
0
cos
θ
0
0
0
0
1
]
\bf{RotationY}= \begin{bmatrix} \cos{\theta} & 0 & \sin{\theta} & 0 \\ 0 & 1 & 0 & 0\\ -\sin{\theta} & 0 & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
RotationY=⎣⎢⎢⎡cosθ0−sinθ00100sinθ0cosθ00001⎦⎥⎥⎤
这是右手系的变换矩阵,
θ
\theta
θ是从
y
y
y轴负向看向正向顺时针的旋转角度。
绕
z
z
z轴的旋转矩阵:
R
o
t
a
t
i
o
n
Y
=
[
cos
θ
−
sin
θ
0
0
sin
θ
cos
θ
0
0
0
0
0
0
0
0
0
1
]
\bf{RotationY}= \begin{bmatrix} \cos{\theta} & -\sin{\theta} & 0 & 0 \\ \sin{\theta} & \cos{\theta} & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
RotationY=⎣⎢⎢⎡cosθsinθ00−sinθcosθ0000000001⎦⎥⎥⎤
这是右手系的变换矩阵,
θ
\theta
θ是从
z
z
z轴负向看向正向顺时针的旋转角度。
绕任意轴的旋转矩阵:
假设一个轴的向量
R
=
(
R
x
,
R
y
,
R
z
)
\bf{R}=(\bf{R_x}, \bf{R_y},R_z)
R=(Rx,Ry,Rz),现在绕这个轴进行旋转,那么变换矩阵应该是:
R
o
t
a
t
i
o
n
Y
=
[
cos
θ
+
R
x
2
(
1
−
cos
θ
)
R
x
R
y
(
1
−
cos
θ
−
R
z
sin
θ
)
R
x
R
z
(
1
−
cos
θ
+
R
y
sin
θ
)
0
R
y
R
x
(
1
−
cos
θ
+
R
z
sin
θ
)
cos
θ
+
R
y
2
(
1
−
cos
θ
)
R
y
R
z
(
1
−
cos
θ
−
R
x
sin
θ
)
0
R
z
R
x
(
1
−
cos
θ
−
R
z
sin
θ
)
R
z
R
y
(
1
−
cos
θ
+
R
x
sin
θ
)
cos
θ
+
R
z
2
(
1
−
cos
θ
)
1
0
0
0
1
]
\bf{RotationY}= \begin{bmatrix} \cos{\theta}+R_{x}^2(1-\cos{\theta}) & R_xR_y(1-\cos{\theta}-R_z\sin{\theta}) & R_xR_z(1-\cos{\theta}+R_y\sin{\theta}) & 0 \\ R_yR_x(1-\cos{\theta}+R_z\sin{\theta}) & \cos{\theta}+R_{y}^2(1-\cos{\theta}) & R_yR_z(1-\cos{\theta}-R_x\sin{\theta}) & 0 \\ R_zR_x(1-\cos{\theta}-R_z\sin{\theta}) & R_zR_y(1-\cos{\theta}+R_x\sin{\theta}) & \cos{\theta}+R_{z}^2(1-\cos{\theta}) & 1 \\ 0 & 0 & 0 & 1 \end{bmatrix}
RotationY=⎣⎢⎢⎡cosθ+Rx2(1−cosθ)RyRx(1−cosθ+Rzsinθ)RzRx(1−cosθ−Rzsinθ)0RxRy(1−cosθ−Rzsinθ)cosθ+Ry2(1−cosθ)RzRy(1−cosθ+Rxsinθ)0RxRz(1−cosθ+Rysinθ)RyRz(1−cosθ−Rxsinθ)cosθ+Rz2(1−cosθ)00011⎦⎥⎥⎤
一般的建模方法
矩阵乘法是不可逆的,因此不能直接随便交换变换的顺序。先旋转再平移和先平移再旋转的效果是不同的;同样的,绕不同的轴旋转的次序不同,得到的结果可能也是不同的。一般来说,应该先执行局部的旋转,再执行局部坐标系的平移。
建模时,应该以物体为参考中心,选定一个物体的局部坐标中心 l o c a l C e n t e r \bf{localCenter} localCenter,使用齐次坐标系表示,然后再给出物体各个定点在局部坐标系中的位置,局部坐标累计上局部旋转在累计上局部中心的坐标,即可生成全局的坐标。
注意,一定是先进行局部坐标系的旋转,再执行平移;否则结果错误。还有,这里使用的列向量的机制,矩阵乘法是从右向左结合的,那么变换矩阵是:
w
o
r
l
d
P
o
s
=
O
f
f
s
e
t
∗
R
o
t
a
t
i
o
n
∗
l
o
c
a
l
P
o
s
\bf{worldPos}=\bf{Offset}*Rotation*localPos
worldPos=Offset∗Rotation∗localPos
Github示例代码,纯C++实现的渲染管线:
https://github.com/StudentErick/PipeLine