上接文章:《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⎠⎟⎟⎟⎟⎟⎟⎟⎟⎞⎝