《Real-Time Rendering》第四版学习笔记——Chapter 4 Transforms(二)
作者:互联网
上接文章:《Real-Time Rendering》第四版学习笔记——Chapter 4 Transforms(一)
四、顶点混合
顶点混合(vertex blending)是为了解决静态物体无法产生柔和的关节变换的问题。顶点混合也称为线性混合蒙皮(linear-blend skinning)、遮罩(enveloping)、骨骼子空间变换(skeleton-subspace deformation)。
主要思路是对关节部分顶点使用不同的变换矩阵,更进一步的是对部分单个顶点应用多个变换矩阵。这意味着最终顶点位置是一个加权混合的结果。
所有的点都收到一个以上变换矩阵影响的顶点网格称为附着在骨骼之上的蒙皮。
从数学层面来看,假设
p
\mathbf p
p是原顶点,
u
(
t
)
\mathbf u(t)
u(t)是基于时间
t
t
t变换后的顶点,那么可计算为:
∑
i
=
0
n
−
1
w
i
B
i
(
t
)
M
i
−
1
p
,
where
∑
i
=
0
n
−
1
w
i
=
1
,
w
i
≥
0
\sum_{i=0}^{n-1}w_i\mathbf B_i(t)\mathbf M_i^{-1}\mathbf p,\ \textrm{where}\ \sum_{i=0}^{n-1}w_i=1,\ w_i\ge 0
i=0∑n−1wiBi(t)Mi−1p, where i=0∑n−1wi=1, wi≥0
其中,
p
\mathbf p
p会受到
n
n
n个骨骼影响;
w
i
w_i
wi是第
i
i
i个骨骼对顶点
p
\mathbf p
p的影响权重;矩阵
M
i
\mathbf M_i
Mi表示从原本的骨骼坐标系转换至世界坐标系的变换;通常情况下,骨骼会将其控制点置于其坐标系的原点;
B
i
(
t
)
\mathbf B_i(t)
Bi(t)表示第
i
i
i个骨骼的基于时间
t
t
t的移动物体的变换。
顶点混合非常适合使用GPU,网格中的顶点集可以存储在静态缓存中,只传递一次并可复用。对于每一帧来说,变化的只是随着一个计算网格的顶点着色器穿入的骨骼变换矩阵;或者使用纹理来存储骨骼变换,来避免超过寄存器限制。
使用基础的顶点混合会导致一些错误的折叠、扭转、自交等。可以使用一种更好的方法,叫做双四元数(dual quaternions)。
五、影像变形
在动画中,影像变形是非常有用的技术。假设在 t 0 t_0 t0时间现实一个模型,在 t 1 t_1 t1时间变为另一个模型,那么在 t 0 t_0 t0到 t 1 t_1 t1这个时间段内,通过某种插值方式,会生成一些列连续的“混合”模型。
影像变形主要需要解决两个问题:顶点对应(vertex correspondence)问题和插值问题。
六、几何缓存回放
使用场景:在一些需要极高质量动画剪辑中,物体运动无法通过其他方法生成,只能保存每帧的每个顶点数据,从硬盘中读取并更新网格。在这种情况下,需要一些方法来减小内存消耗。
首先应用的是量化(quantization),例如使用16位整型来保存顶点位置和纹理坐标。以及使用一些数据压缩算法来进一步减少数据量。
七、投影
在实际渲染一个场景之前,所有场景中相关物体都需要投影到某些平面或简单空间中。之后再进行裁剪和渲染。
7.1 正交投影
正交投影到特点是平行线在投影之后仍然保持平行。
常用的正交投影矩阵通过六元组
(
l
,
r
,
b
,
t
,
n
,
f
)
(l,r,b,t,n,f)
(l,r,b,t,n,f)来表示左、右、下、上、近、远平面。这个矩阵通过缩放和平移,将六元组表示的轴对齐包围盒(axis-aligned bounding box, AABB)变换至原点为中心的轴对齐立方体。
AABB的最小角为
(
l
,
b
,
n
)
(l,b,n)
(l,b,n),最大为
(
r
,
t
,
f
)
(r,t,f)
(r,t,f)。需要注意的是,由于是看向负
z
z
z轴方向,所以
n
>
f
n>f
n>f。
对于OpenGL来说,变换后的立方体最小角为
(
−
1
,
−
1
,
−
1
)
(-1,-1,-1)
(−1,−1,−1),最大角为
(
1
,
1
,
1
)
(1,1,1)
(1,1,1);DirectX则是
(
−
1
,
−
1
,
0
)
(-1,-1,0)
(−1,−1,0)和
(
1
,
1
,
1
)
(1,1,1)
(1,1,1)。该立方体称为规则观察体(canonical view volume),其坐标系称为标准化设备坐标系(normalized device coordinates)。
正交投影矩阵为:
P
o
=
S
(
s
)
T
(
t
)
=
(
2
r
−
l
0
0
0
0
2
t
−
b
0
0
0
0
2
f
−
n
0
0
0
0
1
)
(
1
0
0
−
l
+
r
2
0
1
0
−
t
+
b
2
0
0
1
−
f
+
n
2
0
0
0
1
)
=
(
2
r
−
l
0
0
−
r
+
l
r
−
l
0
2
t
−
b
0
−
t
+
b
t
−
b
0
0
2
f
−
n
−
f
+
n
f
−
n
0
0
0
1
)
\mathbf P_o=\mathbf S(\mathbf s)\mathbf T(\mathbf t)=\begin{pmatrix}\cfrac{2}{r-l} & 0 & 0 & 0\\ 0 & \cfrac{2}{t-b} & 0 & 0\\ 0 & 0 & \cfrac{2}{f-n} & 0\\ 0 & 0 & 0 & 1\end{pmatrix}\begin{pmatrix}1 & 0 & 0 & -\cfrac{l+r}{2}\\ 0 & 1 & 0 & -\cfrac{t+b}{2}\\ 0 & 0& 1 & -\cfrac{f+n}{2}\\ 0 & 0 & 0 & 1\end{pmatrix}\\ =\begin{pmatrix}\cfrac{2}{r-l} & 0 & 0 & -\cfrac{r+l}{r-l}\\ 0 & \cfrac{2}{t-b} & 0 & -\cfrac{t+b}{t-b}\\ 0 & 0 & \cfrac{2}{f-n} & -\cfrac{f+n}{f-n}\\ 0 & 0 & 0 & 1\end{pmatrix}
Po=S(s)T(t)=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l20000t−b20000f−n200001⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎜⎜⎜⎛100001000010−2l+r−2t+b−2f+n1⎠⎟⎟⎟⎟⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l20000t−b20000f−n20−r−lr+l−t−bt+b−f−nf+n1⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞
在计算机图形学中,在投影后更常用左手坐标系,所以这里需要一个镜像变换:
P
=
(
1
0
0
0
0
1
0
0
0
0
−
1
0
0
0
0
1
)
\mathbf P=\begin{pmatrix}1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & -1 & 0\\ 0 & 0 & 0 & 1\end{pmatrix}
P=⎝⎜⎜⎛1000010000−100001⎠⎟⎟⎞
DirectX使用
[
0
,
1
]
[0,1]
[0,1]范围的深度值,而不是OpenGL的
[
−
1
,
1
]
[-1,1]
[−1,1],所以这里还需要一个缩放和平移:
M
s
t
=
(
1
0
0
0
0
1
0
0
0
0
0.5
0.5
0
0
0
1
)
\mathbf M_{st}=\begin{pmatrix}1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0.5 & 0.5\\ 0 & 0 & 0 & 1\end{pmatrix}
Mst=⎝⎜⎜⎛10000100000.50000.51⎠⎟⎟⎞
所以DirectX的正交投影矩阵为:
P
o
[
0
,
1
]
=
(
2
r
−
l
0
0
−
r
+
l
r
−
l
0
2
t
−
b
0
−
t
+
b
t
−
b
0
0
1
f
−
n
−
n
f
−
n
0
0
0
1
)
\mathbf P_{o[0,1]}=\begin{pmatrix}\cfrac{2}{r-l} & 0 & 0 & -\cfrac{r+l}{r-l}\\ 0 & \cfrac{2}{t-b} & 0 & -\cfrac{t+b}{t-b}\\ 0 & 0 & \cfrac{1}{f-n} & -\cfrac{n}{f-n}\\ 0 & 0 & 0 & 1\end{pmatrix}
Po[0,1]=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l20000t−b20000f−n10−r−lr+l−t−bt+b−f−nn1⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞
且更常见的形式是其转置,因为DirectX使用行优先形式的矩阵。
7.2 透视投影
在透视投影中,平行线将不再平行,它们会在其延长线上汇聚到一点。透视投影更符合真实世界,也就是近大远小。
假设相机置于原点,视锥台的近平面和远平面为
z
=
n
,
z
=
f
,
0
>
n
>
f
z=n,\ z=f,\ 0>n>f
z=n, z=f, 0>n>f。近平面的矩形最小角为
(
l
,
b
,
n
)
(l,b,n)
(l,b,n),最大角为
(
r
,
t
,
n
)
(r,t,n)
(r,t,n)。
参数
(
l
,
r
,
b
,
t
,
n
,
f
)
(l,r,b,t,n,f)
(l,r,b,t,n,f)决定了相机的视场锥台。视场水平范围取决于左右平面形成的夹角;同理,垂直范围取决于上下平面的夹角。
视场范围是产生场景感官的重要因素,因为眼睛本身有一个物理的视场范围与计算机显示器的关系,为:
ϕ
=
2
arctan
(
w
/
(
2
d
)
)
\phi=2\arctan(w/(2d))
ϕ=2arctan(w/(2d))。其中
ϕ
\phi
ϕ为视场范围,
w
w
w为物体垂直于视线的宽度,
d
d
d是物体的距离。如果视场范围过小,则会导致透视效果降低,产生放大效果;视场范围过大,则会导致畸变,且会使近距离物体夸大尺寸。
透视投影矩阵为:
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
f
n
f
−
n
0
0
1
0
)
\mathbf P_p=\begin{pmatrix}\cfrac{2n}{r-l} & 0 & -\cfrac{r+l}{r-l} & 0\\ 0 & \cfrac{2n}{t-b} & -\cfrac{t+b}{t-b} & 0\\ 0 & 0 & \cfrac{f+n}{f-n} & -\cfrac{2fn}{f-n}\\ 0 & 0 & 1 & 0\end{pmatrix}
Pp=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l2n0000t−b2n00−r−lr+l−t−bt+bf−nf+n100−f−n2fn0⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞
经过投影后的点 p = ( q x , q y , q z , q w ) T \mathbf p=(q_x,q_y,q_z,q_w)^T p=(qx,qy,qz,qw)T,其中的 w w w分量 q w q_w qw通常不为0且不为1,需要除以 q w q_w qw,得到投影点: p = ( q x / q w , q y / q w , q z / q w , 1 ) T \mathbf p=(q_x/q_w,q_y/q_w,q_z/q_w,1)^T p=(qx/qw,qy/qw,qz/qw,1)T。
OpenGL需要对
z
z
z镜像,得到
0
<
n
′
<
f
′
0<n^{\prime}<f^{\prime}
0<n′<f′:
P
OpenGL
=
(
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
f
′
n
′
f
′
−
n
′
0
0
−
1
0
)
\mathbf P_{\textrm{OpenGL}}=\begin{pmatrix}\cfrac{2n^{\prime}}{r-l} & 0 & \cfrac{r+l}{r-l} & 0\\ 0 & \cfrac{2n^{\prime}}{t-b} & \cfrac{t+b}{t-b} & 0\\ 0 & 0 & -\cfrac{f^{\prime}+n^{\prime}}{f^{\prime}-n^{\prime}} & -\cfrac{2f^{\prime}n^{\prime}}{f^{\prime}-n^{\prime}}\\ 0 & 0 & -1 & 0\end{pmatrix}
POpenGL=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l2n′0000t−b2n′00r−lr+lt−bt+b−f′−n′f′+n′−100−f′−n′2f′n′0⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞
如果使用视场范围
ϕ
\phi
ϕ、长宽比
a
=
w
/
h
a=w/h
a=w/h、
n
′
n^{\prime}
n′和
f
′
f^{\prime}
f′,则透视投影矩阵为:
P
OpenGL
=
(
c
/
a
0
0
0
0
c
0
0
0
0
−
f
′
+
n
′
f
′
−
n
′
−
2
f
′
n
′
f
′
−
n
′
0
0
−
1
0
)
\mathbf P_{\textrm{OpenGL}}=\begin{pmatrix}c/a & 0 & 0 & 0\\ 0 & c & 0 & 0\\ 0 & 0 & -\cfrac{f^{\prime}+n^{\prime}}{f^{\prime}-n^{\prime}} & -\cfrac{2f^{\prime}n^{\prime}}{f^{\prime}-n^{\prime}}\\ 0 & 0 & -1 & 0\end{pmatrix}
POpenGL=⎝⎜⎜⎜⎜⎛c/a0000c0000−f′−n′f′+n′−100−f′−n′2f′n′0⎠⎟⎟⎟⎟⎞
其中,
c
=
1.0
/
tan
(
ϕ
/
2
)
c=1.0/\tan(\phi/2)
c=1.0/tan(ϕ/2)。
例如DirectX这类将近平面映射到
z
=
0
z=0
z=0,远平面映射到
z
=
1
z=1
z=1,且为左手坐标系,则其透视投影矩阵为:
P
p
[
0
,
1
]
=
(
2
n
′
r
−
l
0
−
r
+
l
r
−
l
0
0
2
n
′
t
−
b
−
t
b
t
−
b
0
0
0
f
′
f
′
−
n
′
−
f
′
n
′
f
′
−
n
′
0
0
1
0
)
\mathbf P_{p[0,1]}=\begin{pmatrix}\cfrac{2n^{\prime}}{r-l} & 0 & -\cfrac{r+l}{r-l} & 0\\ 0 & \cfrac{2n^{\prime}}{t-b} & -\cfrac{t_b}{t-b} & 0\\ 0 & 0 & \cfrac{f^{\prime}}{f^{\prime}-n^{\prime}} & -\cfrac{f^{\prime}n^{\prime}}{f^{\prime}-n^{\prime}}\\ 0 & 0 & 1 & 0\end{pmatrix}
Pp[0,1]=⎝⎜⎜⎜⎜⎜⎜⎜⎜⎛r−l2n′0000t−b2n′00−r−lr+l−t−btbf′−n′f′100−f′−n′f′n′0⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞
使用透视投影矩阵的其中一个影响是,深度值不是线性变换的。假设使用OpenGL的透视投影矩阵,则变换后的点坐标为: v = P p = ( . . . . . . d p z + e ± p z ) \mathbf v=\mathbf{Pp}=\begin{pmatrix}...\\...\\dp_z+e\\\pm p_z\end{pmatrix} v=Pp=⎝⎜⎜⎛......dpz+e±pz⎠⎟⎟⎞,其中 d = − ( f ′ + n ′ ) ( f ′ − n ′ ) , e = − 2 f ′ n ′ / ( f ′ − n ′ ) d=-(f^{\prime}+n^{\prime})(f^{\prime}-n^{\prime}),\ e=-2f^{\prime}n^{\prime}/(f^{\prime}-n^{\prime}) d=−(f′+n′)(f′−n′), e=−2f′n′/(f′−n′),转换为标准设备坐标后(NDC)可得: z N D C = d p z + e − p z = d − e p z , z N D C ∈ [ − 1 , + 1 ] z_{NDC}=\cfrac{dp_z+e}{-p_z}=d-\cfrac{e}{p_z},\ z_{NDC}\in [-1,+1] zNDC=−pzdpz+e=d−pze, zNDC∈[−1,+1]。由此可见,变换后的深度与原深度成反比。而且近平面和远平面的设置,会影响到深度值的精度。
提升深度值精度的常用方法为reversed z,即存储 1.0 − z N D C 1.0-z_{NDC} 1.0−zNDC。
标签:Real,Chapter,00,mathbf,prime,Rendering,cfrac,pmatrix,顶点 来源: https://blog.csdn.net/dongzhong1990/article/details/122783245