图形学——网格简化

上一篇博客讲了网格的相关内容,也提到了一些网格简化的算法。但是实际上网格简化是网格中很大的一个块,因此这次单独拿出一篇文章来介绍简化的相关内容。

话说到前面,网格简化有很多方法,这里介绍的多种方法,我也不是都明白,以后有机会的话会对其进行各个算法复现。对于一些复杂的算法,可能需要查阅相关论文才能明白其中的数学道理,在本文也会给出一些复杂的简化算法的论文链接。

网格简化更宽泛,具体的细节一般为层次细节简化(LOD:Level of Details)。它主要用于简化采样密集的多面体网格,以及三维场景的存储,传输和绘制。它主要目标是在不影响画面视觉效果的条件下,通过逐次简化景物的表面细节来减少场景的几何复杂度,从而提高绘制算法的效率。

在层次细节简化时候,我们会对一个原始的多面体模型建立几个不同逼近程度的几何模型。从近处观察物体,采用精细模型,从远处观察物体,采用较为粗糙的模型,当视点连续变化时,在两个不同层次的模型间会有明显的跳跃,为了追求完美应该要一个平滑的过渡,因此还需要几何形状过渡。

因此层次细节简化技术的研究主要集中在:

  1. 建立不同层次细节的模型。对任意给定的复杂多边形网格MM,由精细到粗糙建立一系列模型序列:M0,M1,,Mn,M0=MM_0,M_1,…,M_n, M_0 = M
  2. 建立相邻层次的多边形网格Mi,Mi+1M_i,M_ {i+1}的几何形状过渡:Φ:MiMi+1\Phi: M_i \rightarrow M_ {i+1}

层次细节简化第一步说到底就是不同程度的网格简化。下面介绍几个简化的基本操作,和上篇文章相似。

  • 顶点删除操作L删除网格中的一个顶点,对它的相邻三角形形成的空洞做三角剖分

  • 边压缩操作:网格上一条边压缩为一个顶点,与该边相邻的两个三角形退化为边

  • 面片收缩操作:网格上的一个面片收缩为一个顶点,该三角形本身退化为点,其相邻的三个三角形都退化为边

下面会介绍几个具体的算法,他们策略不同,但是最后都是上面3种操作的组合。

基于长方体滤波的多面体简化

基于长方体滤波的多面体简化是最简单的一种简化方法,它的思想是,将多个聚集在一起的顶点聚合为一个顶点,这个操作实际上是采样的一种。具体步骤如下:

  • 给定一个多面体MM,记KK为其拓扑,假设MM已三角化。算法首先建立MM的长方体包围盒,并将该包围盒所包围的空间均匀剖分成一系列的小长方体子空间,然后将各个子空间中的顶点聚合为一个代表顶点,如果子空间中无顶点则不考虑。一般来说聚合的这个顶点位于子空间的中心,子空间也是被均匀分割的。这些代表顶点为原多面体所示景物的重新采样。基于原多面体的拓扑结构和这些采样点可以重新产生多面体,所得多面体即为保持一定层次细节的模型。剖分的子空间越小,得到的层次模型就越逼近原多面体。

在二维空间中,长方体滤波的示意如下:

长方体滤波的主要缺点是会导致一些重要的高频细节的丢失。

下面两个算法是利用顶点删除技术的代表。顶点删除技术比较难的部分在于,删除哪些顶点?随机采样不能得到很好的效果。在网格中,近似平面的顶点可以删除,而对于尖锐部分的顶点删除后会使得模型大大折扣。因此下面介绍两个局部判别准则,用来决定删除哪些顶点比较合适。

基于相邻面片和边界的局部平坦性原则

由Schroeder提出。该判别分两种情况:

  • 对于网格内部顶点VV,记其周围相邻面片集为SS,则该点的平坦性标准由下述的距离来描述: d=|𝑵(vC)|d = \vert\mathbf N \cdot(v - C) \vert 其中NN为向量fS𝒏(f)A(f)fSA(f)\frac{\sum_ {f\in S}\mathbf n (f)A(f)}{\sum_ {f\in S} A(f)}的单位向量,C=fSc(f)A(f)A(f)C = \frac{\sum_ {f\in S}c(f)A(f)}{A(f)},这里A(f),c(f),𝒏(f)A(f),c(f),\mathbf{n}(f)分别为三角面片的面积,中心和法向量。
    这种度量方式某种程度上反映了该顶点的突出程度。
  • 对于边界顶点vv,记与它相邻的两个边界顶点为v1,v2v_1,v_2,则其平坦性标准定义为vvv1v2v_1v_2连线的距离。

采用等距面来限定简化模型顶点的变化范围

由Cohen提出,这个算法相对于上述会更复杂一点。

首先介绍一个概念,叫包络(envelope)。多边形网格表面PP可以看作一张分片线性参数曲面: r(u,v)=(rx(u,v),ry(u,v),rz(u,v)) r(u,v) = (r_x(u,v),r_y(u,v),r_z(u,v)) 其单位法向量为: 𝒏(u,v)=(nx(u,v),ny(u,v),nz(u,v)). \mathbf n(u,v) = (n_x(u,v), n_y(u,v),n_z(u,v)). 对于给定的ϵ>0\epsilon > 0PP的三维ϵ\epsilon等距面定义为: rϵ(u,v)=r(u,v)+ϵ𝒏(u,v) r^{\epsilon}(u,v) = r(u,v) + \epsilon \mathbf{n}(u,v) 近似定义原始多边形网格PP沿正负发现的ϵ\epsilon等距面P(+ϵ),P(ϵ)P(+\epsilon),P(- \epsilon)ϵ\epsilon等距面P(+ϵ)P(+\epsilon)P(ϵ)P(-\epsilon)上对应顶点vi+,viv_i^+,v_i^-及其法向量可表示为: vi+=vi+ϵ𝒏i,vi=viϵ𝒏i,𝒏i+=𝒏i=𝒏 v_i^+ = v_i + \epsilon\mathbf{n}_i, v_i^- = v_i - \epsilon \mathbf{n}_i, \mathbf{n}_i^+ = \mathbf{n}_i^- = \mathbf{n} 上述方法产生的P±ϵP_ {\pm \epsilon}可能出现自交的现象。

我们用解析法计算ϵ\epsilon。现在来考察PP上任意三角形v1v2v3\triangle v_1v_2v_3

PP上每个与三角形v1v2v3\triangle v_1v_2v_3不相邻的三角面片j\triangle_j,判断j\triangle_j是否与v1v2v3\triangle v_1v_2v_3的基本柱体相交。可计算得到qjq_jv1v2v3\triangle v_1v_2v_3的距离σj\sigma_j

这两个等距面构成了表面的包络。Cohen顶点删除的过程如下:

  • 利用贪婪搜索策略,将原表面PP的所有顶点列入带处理的顶点队列。
  • 对当前待处理顶点队列中的一顶点,算法尝试从PP上删除该顶点及该顶点直接相邻的三角面片,并试图用某种类型的三角剖分法来填补顶点删除后在表面PP上形成的空洞。
  • 若空洞位于PP的包络内且能成功地被填补,则从当前队列中删除该顶点,简化表面PP模型并重构原来与该顶点相连接的各顶点的拓扑关系,否则,该顶点从当前的待处理队列中退出,表面PP保持不变。上述过程知道待处理顶点队列变空为止。

这个算法的相关论文为:envelopes

Hoppe-渐进的网格简化技术

由Hoppe提出。这个算法比较难以理解,定义了很多新的东西去讲解这个算法。在渐进网格算法中,任一网格M̂\hat M可表示为一粗网格以及nn个逐步细化网格Mi(i=1,,n)M^i(i=1,…,n)的变换,且有M̂=Mn\hat M = M^n。一张网格可以定义为一个二元组:(K,V)(K,V)。其中KK是一个单纯复形(simplicical complex),它表示了MM的顶点,边和面的邻接关系。

V=viR3|i=1,,mV = {v_i \in R^3\vert i=1,…,m}MM的顶点位置向量集,它定义了网格MMR3R^3中的形状。而单纯复形KK由顶点集以及称之为单形的非空子集组成:

0-单形{i}K\{i\} \in K,即为顶点,1-单形{i,j}K\{i,j\} \in K是一条边,2-单形{i,j,k}K\{i,j,k\}\in K为一个面。需要注意的是单纯复形KK并不包含VV的所有成员,只包含了构造网格MM所需要的所有面,边和顶点的子集。为了在结构上刻画单纯复形,我们引进拓扑实现(topological realization)|K|\vert K\vert的概念。

若将顶点{i}(i=1,2,,m)\{i\}(i=1,2,…,m)看成为RmR^m中的基向量ei={0,,0,i,0,,0}e_ {i} = \{0,…,0,i,0,…,0\},则定义在RmR^m中的集合|K|\vert K \vertKK的拓扑实现: |K|=sinK|s| \vert K \vert = \bigcup_ {s \ in K}\vert s\vert 其中ssKK的一个单形,|s|\vert s \vert为s在RmR^m空间中的顶点的凸包。

我们记ϕv=ϕ|K|\phi_v = \phi_ {\vert K\vert},称为v1v2v3\triangle v_1v_2v_3R3R^3中的几何实现(geometric realization)。若ϕv(|K|)\phi_v(\vert K\vert)不自交,则ϕv\phi_v为1-1映射。此时,ϕv\phi_v为一嵌入映射,即对pϕv(|K|)\forall p \in \phi_v(\vert K \vert),存在唯一mm维向量b|K|b \in \vert K\vert,使得p=ϕv(b)p = \phi_v(b)。我们称bbpp关于单纯复形的重心坐标向量。实际上,bb可表示为: b=i=1mbiei b = \sum_ {i=1}^mb_ie_i 容易知道,当MM为一三角网格时,ϕv(|K|)\phi_v(\vert K\vert)上任一点的重心坐标向量bb中至多只有三个分量非零。

有了上述定义,Hoppe采用显示能量函数E(M)E(M)来度量简化网格与原始网格的逼近度: E(M)=Edist(M)+Espring(M)+Escalar(M)+Edisc(M). E(M) = E_ {dist}(M) + E_ {spring} (M) + E_ {scalar}(M) + E_ {disc}(M). 上式中,E(M)E(M)MM的距离度量,定义为点集X={x1,,xn}X = \{x_1,…,x_n\}到网格MM的距离平方: Edist(M)=i=1nd2(xi,ϕv(|K|)) E_ {dist}(M) = \sum_ {i=1}^n d^2(x_i,\phi_v(\vert K \vert)) EspringE_ {spring}MM的弹性能量,这相当于在MM的每条边上均匀放置一条弹性系数为kk的弹簧,即: Espring(M)=(i,j)Kkvi,vj2 E_ {spring} (M) = \sum_ {(i,j) \in K}k\Vert v_i,v_j \Vert^2 Escalar(M)E_ {scalar}(M)度量M的标量属性的精度,而Edisc(M)E_ {disc}(M)则度量了MM上视觉不连续的特征线(如边界线,侧影轮廓线等)的几何精度。

Hoppe利用边收缩变换来逐步迭代计算上述能量的优化过程。下面是一个边收缩变换的例子:

因此,初始网格M̂=Mn\hat M = M^n可经过nn组的边收缩变形后简化为M0M^0M̂=Mn...M0 \hat M = M^n\rightarrow ... \rightarrow M^0 边收缩变换的逆变换被称为顶点分裂变换,也就是将顶点分裂成一条边。

这个简化技术的过程我不是很清楚,而老师上课的时候说的也不清楚,很多符号之前并没有定义。我去查找了这篇文章,把链接放在这里,如果想要详细了解请戳:Progress Meshes

基于二次误差度量的简化技术

Hoppe的边收缩操作可推广为一般的顶点合并变换来描述(v1,v2)v(v_1,v_2)\rightarrow v。而Garland和Heckbert引进了二次误差度量来刻画每一个顶点移动后引起的误差,对表面上的每一个顶点vav_a均有许多三角面片与之相邻,记plane(va)plane(v_a)为这些三角形所在平面所构成的集合,即: plane(va)={(a,b,c,d)|ax+by+cz+d=0,(a2+b2+c2=1)is the coefficients of the adjacent plane of v_a} plane(v_a) = \left\{(a,b,c,d)\vert ax+by+cz+d = 0,(a^2 +b^2 +c^2 =1) \text{is the coefficients of the adjacent plane of v_a} \right\} 我们采用如下的二次函数来度量vav_a移动vv时产生的误差: Δ(vav)=pplane(va)(pvT)2 \Delta(v_a \rightarrow v) = \sum_ {p \in plane(v_a)}(pv^T)^2 其中v=(x,y,z,1)v = (x,y,z,1)为齐次坐标,展开上式可以得到: Δ(vav)=pplane(va)(pvT)2v(pplane(va)Kp)vT=vQ(va)vT \begin{aligned} \Delta(v_a \rightarrow v) &= \sum_ {p \in plane(v_a)}(pv^T)^2\\ v \left(\sum_ {p \in plane(v_a) K_p}\right)v^T = vQ(v_a)v^T \end{aligned} 式中: $$ K_p = p^Tp \begin{bmatrix} a^2 & ab &ac & ad\\ ab & b^2 &bc & bd\\ ac & bc & c^2 & cd\\ ad& bd & cd & d^2 \end{bmatrix},\\ Q(v_a) = \sum_ {p \in plane(v_a)} K_p $$ 这样,每一顶点vav_a,在预处理时,可以用上述方法计算矩阵Q(va)Q(v_a),从而计算移动误差。而合并顶点每次需要移动两个顶点,因此需要考虑同时移动后形成的误差。而Garland和Heckbert简单地采用加法来刻画多点移动形成的误差,对于(v1,v2)v(v_1,v_2) \rightarrow v,其误差为: Δ(v)=Δ(v1v)+Δ(v2v)=v(Q(v1)+Q(v2))vT=vQvT \Delta (v) = \Delta (v_1 \rightarrow v) + \Delta (v_2 \rightarrow v) = v(Q(v_1) + Q(v_2))v^T = vQv^T。 因此应该选取vv使得误差达到最小。一般想法是采取优化方法如梯度下降等等,但是在这个式子里,一般vv的值是有解析解的,正如线性回归一样,我们可以得到唯一解,或者伪逆技术(pseudo-inverse)求出解。如果伪逆技术失败,简单选取vvv1,v2v_1,v_2或者v1+v22\frac{v_1 + v_2}{2}

二次误差度量简化算法的论文是:Surface Simplification Using Quadric Error Metrics