Computer Graphics note(5):Geometry

一.Represent Geometry

几何主要分两种,一种是implicit geometry,另一种是explicit geometry。不同的几何有不同的表示方法。

1."Implicit" Representations of Geometry

隐式的意思是对点进行归类,也就是告诉我们点满足的关系。比如对一个单位球体,则所有满足\(x^2+y^2+z^2=1\)的点都在球面上,这就是单位球体的隐式表示。推广到一般情况上,当有一个关系(函数)\(f(x,y,z)=0\)表示几何所表述的面,某点满足关系则认为该点在其面上。

对于隐式几何表达而言,有时不容易看出对应的几何形体。比如对于关系(圆环结构)\(f(x,y,z)=(2-\sqrt{x^2+y^2})^2+z^2-1\),就很难看出什么样的点在对应的几何表面上。但是隐式表达很容易判断点是否在几何表面上,只要将点带入对应关系式子中计算即可。

(1)algebraic surface

隐式表达有很多种,比如algebraic surface,也就是用数学表达来表示曲面,但是这样的表达不直观,如下图

(2)CSG

Constructive Solid Geometry(CSG),通过定义基本几何的布尔运算来定义新的几何,如下图

(3)Distance Function

Distance Function,也就是使用距离函数来描述空间中任意点到几何表面的最短距离(可正可负)。它可以用于blending一个移动的边界,如下图所示:

假设输入和输出都是几何形体的边界,A,B都是从视口看去几何形体被遮住的样子,A被遮住\(\frac{1}{3}\),B被遮住\(\frac{2}{3}\)。现在希望求出从A到B的中间状态,如果直接进行线性blend就会得到图blend(A,B),其中左\(\frac{1}{3}\)为黑色重叠部分,中间为灰色,右边为白色。这与期望状态不符合,中间的运动状态应该还是左边为黑色,右边为白色。
此时考虑使用距离函数(Signed Distance Function,符号距离函数 ),求出SDF(A),SDF(B),如图所示,边界上是0,设置左边为负,右边为正,然后求出blend(SDF(A),SDF(B)),这相当于blend两张图的边界(如下图所示),对最后结果还原成几何表面即可(距离函数被blend之后会得到新的距离函数,用新函数找出所有值为0的位置即可)。

(4)Level Set Methods(水平集)

水平集(不限于2D)和距离函数的区别就是,它的函数值填充在格子中,但是最后仍然需要找到其值为0的位置。如下图所示\(f(x)=0\)的位置(可用Bilinear interpolation算出)连成一条线即为物体的边界,不同位置有着相同的值(类比等高线)。

水平集可以用于3D医疗数据,如下图,假设一个纹理表示人体表面不同位置的密度,则就可以找出密度函数等于某个特定值来找出相应的表面。

还可以用于物理模拟,如下图,对于水滴之间的融合就可以使用水平集或者距离函数来进行blend,然后提取出blend之后的物体表面。

(5)Fractals(分形)

分形是指自相似,即部分和整体相似(类比递归),例子如下图。

2."Explicit" Representations of Geometry

显示表达是将所有的点都直接给出或者通过参数映射的方式来表示几何形体。其中参数映射是通过某个关系将点映射到空间中某点,如下图:

显示表达可以知道对应空间中的形状,但是不能判断点是否在表面内。显示表达有很多种,下面是两个例子。

(1).Point Cloud

点云是最简单的显示表达方法,不考虑物体的表面,而是将其表示成为一个点集\((x,yz)\),点云的密度决定了物体表示的效果。理论上点云可以表示任何不同类型的几何模型。
点云的数据通常是通过扫描得到的,所以涉及到如何将点云变成Polygon Mesh(多边形面)的问题。

(2).Polygon Mesh

使用多边形网格(通常使用的是三角形面和四边形面)来描述物体(图形学中经常使用),如下图:

<1>The Wavefront Object File (.obj) Format

这涉及到如何表示用三角形面形成的物体,通常使用Wavefront Object File(.obj)格式文件(和编译出的.obj文件无关)来表示,一个文件表示一个物体。该文件是一个文本文件,其中定义了vertices、normlas、texture coordinates and their connectivities

如上图所示例子,该文件所描述的是一个立方体,故有8个顶点,用v x y z表示;
立方体有六个面,即有六个朝向,所以有6种不同的法线,用vn x y z表示,上面例子中虽然有8行,但是这是由于自动建模引起的冗余问题(比如,在不考虑数字精度问题情况下,29和30行表示的其实是同一种法线);
对于立方体6个面,最多可以定义24个纹理坐标,考虑到顶点共用的问题,这里定义了12个,用vt u v表示;
最后定义的是三角形之间的连接关系(顶点可以共用),用face表示,格式为f v_index/vt_index/vn_index,这里的index指的是文件中定义的顺序坐标,即该v/vn/vt是从上往下数第几个。f 5/1/1 1/2/1 4/3/1的意思是使用文件中定义的第5、1、4个顶点组成一个三角形,其中第5个顶点使用的是第1个纹理坐标和第1种法线,第1个顶点使用第2个纹理坐标和第1种法线,同理第4个顶点使用的是第3个纹理坐标和第1种法线。

二.Curves

1.Bézier curves

贝塞尔曲线也是显示表达的一种,其思想是使用一系列控制点来定义曲线,这些控制点能使得曲线满足某些性质。例子如下图:

上图是4个控制点定义的三次贝塞尔曲线,曲线起始于\(P_0\)走向\(P_1\),并从\(P_2\)的方向来到\(P_3\)。曲线一般不会经过\(P_1\)\(P_2\),这两个点只是提供方向信息。\(P_0\)\(P_1\)之间的间距,决定了曲线在转san向\(P_2\)之前,走向\(P_1\)方向的“长度有多长”。

2.绘制Bézier Curves(de Casteljau Algorithm)

还需要考虑的是如何绘制贝塞尔曲线,这里使用的是de Casteljau算法(计算贝塞尔曲线的递归方法)。
以二次贝塞尔曲线(3个控制点)为例,如下图所示,参考三次贝塞尔曲线,该曲线应该是\(\pmb{b_0}\)开始,\(\pmb{b_2}\)结束。

首先定义起点\(b_0\)在时间\(t=0\),终点\(b_2\)在时间\(t=1\)上,则绘制曲线也就是找出给定时间\(t\)时的对应点即可。这也就是de Casteljau算法所要完成的工作。

如上图从左到右所示,给定任意时间\(t\in[0,1]\),使用线性插值\(b_0\)\(b_1\)(假设\(b_0\)\(t=0\)\(b_1\)\(t=1\)上)之间插入点\(b_0^1\),同理在\(b_1\)\(b_2\)之间插入\(b_1^1\),然后在\(b_0^1\)\(b_1^1\)之间插入点\(b_0^2\),最后的点\(b_0^2\)也就是二次贝塞尔曲线在时间\(t\)时所在的位置。实际上就是对一个不断递归的过程,所以只要对\(t\in[0,1]\)进行枚举,就可以绘制出曲线,结果如下图。

对于三次贝塞尔曲线,也是一样的过程,结果如下:

3.Bézier Curve – Algebraic Formula

现在考虑贝塞尔曲线的代数形式,同样以上面的二次贝塞尔曲线为例。

之前说过是使用线性插值\(b_0\)\(b_1\)(假设\(b_0\)\(t=0\)\(b_1\)\(t=1\)上)之间插入点\(b_0^1\),对于其他点同样如此,则有下列式子:

\[\begin{cases} b_0^1(t)&=(1-t)b_0+tb_1 \\ b_1^1(t)&=(1-t)b_1+tb_2 \end{cases}\\ \downarrow \\ b_0^2=(1-t)b_0^1+tb_1^1 \\ \downarrow \\ b_2^0(t)=(1-t)^2b_0+2t(1-t)b_1+t^2b_2,t\in[0,1] \]

对于给定n+1个控制点(此处包含了起止点),则可以得到n阶贝塞尔曲线(\(b^n(t)\)是一个多项式),对于任意时间t,曲线都是之前给定的控制点(\(b_j\))的线性组合,其系数与时间t相关,系数为多项式\(B_j^n{t}\),该多项式称为Bernstein polynomial(伯恩斯坦多项式)。

\[b^n(t)=b_0^n(t)=\sum_{j=0}^n{b_jB_j^n{t}} \]

其中Bernstein polynomial如下,其中\(\begin{pmatrix}n\\i\end{pmatrix}\)是二项式系数。

\[B_i^n(t)=\begin{pmatrix}n\\i\end{pmatrix}t^i(1-t)^{n-i}\\ \begin{pmatrix}n\\i\end{pmatrix}=\frac{n!}{i!(n-i)!} \]

则三次贝塞尔曲线的代数形式如下:

\[{\mathbf {b}}(t)={\mathbf {b}}_{0}(1-t)^{3}+3{\mathbf {b}}_{1}t(1-t)^{2}+3{\mathbf {b}}_{2}t^{2}(1-t)+{\mathbf {b}}_{3}t^{3}{\mbox{ , }}t\in [0,1] \]

4.Properties of Bézier Curves(性质)

贝塞尔曲线有许多性质。

  1. 首先就是必须过起止点,以三次贝塞尔曲线为例,即有\(b(0)=b_0\)\(b(1)=b_3\)
  2. 对于三次贝塞尔曲线而言,起始和结束方向的切线分别是\(b'(0)=3(b_1-b_0)\)\(b'(1)=3(b_3-b_2)\),也就是起始方向由前两个点定义,结束方向由后两个点定义
  3. 仅对于仿射变换(affline transformations=线性变换+平移),先对控制点进行仿射变换然后再绘制出曲线,这与先用控制点绘制出曲线后再对曲线上所有点进行仿射变换是一样的。所以当需要对贝塞尔曲线做仿射变换只需要对控制点进行变换即可。
  4. 凸包(计算几何中的凸包)性质。贝塞尔曲线一定在控制点形成的凸包内

5.Piecewise Bézier Curves(分段贝塞尔曲线)

(1)定义

上面使用了控制点进行绘制贝塞尔曲线,但是这样会存在一个问题,如果需要使用很多控制点来控制一个贝塞尔曲线时很难控制且不直观。所以考虑逐段定义贝塞尔曲线然后进行连接而不是一次性定义的方式。而普遍情况下,逐段定义使用的都是4个控制点(即三次贝塞尔曲线),这样的方法称为Piecewise Cubic Bézier。应用很多,比如PS中的钢笔工具。

(2)保证分段连续(\(C^1\)连续)

这样的情况下还需要保证分段之间是光滑连接的,也就是说曲线连接的地方在切线意义上连续(导数相等),连接点前后切线方向和大小都相同。从性质上可以知道起始方向由前两个点定义,结束方向由后两个点定义,所以只需要后一段曲线的前两个控制点和前一段曲线的后两个控制点保持共线且等距,前段曲线最后控制点也就是后段曲线第一控制点,如下图所示,即b12、b13(b20)、b21三点共线且有|b12-b13|=|b20-b21|

(3)连续性(\(C^0/C^1\))

\(C^0\)连续:第一段曲线终止点\(a_n\)等于第二段曲线的起始点\(b_0\),即\(a_n=b_0\),与所成角度无关。

\(C^1\)连续:在\(C^0\)的基础上,满足连接处前后切线方向大小相同(连接点和前后控制点等距)和方向相同(连接点和前后控制点共线),\(a_n=b_0=\frac{1}{2}(a_{n-1}+b_1)\)

6.B-splines(Basis splines)

Spline(样条)是指被一系列点所构造的连续曲线,任意点处满足一定的连续性。
B-splines指的是基函数样条,具有良好的局部性,即修改某点时可以知道其影响范围。Piecewise Bézier Curves同样具有局部性,但是B-splines具有更好的控制性。B样条及其扩展NURBS比较复杂,不再展开。

三.Surfaces

1.Bézier surfaces

首先在平面上定义4条贝塞尔曲线(4x4个控制点),如下图,以下过程每个曲线都使用de Casteljau算法进行绘制。

对于给定的一个时间\(u\),都可以在4条贝塞尔曲线上找到一点,将这4个点作为新的贝塞尔曲线的控制点。再给定另一个时间\(v\),即可得到贝塞尔曲面在参数\((u,v)\)下的位置(\(u,v\in[0,1]\)),也就是新的贝塞尔曲线上的一点,则遍历时间\(v\)就可以进行绘制新的贝塞尔曲线,如下图。也就是说,\(u\)用来找到4给控制点,\(v\)用来确定新曲线上一点。

遍历时间\(u\),对上图中蓝色的贝塞尔曲线进行"移动",就可以得到贝塞尔曲面,如下图。

2.Mesh(Triangles & quads)

Mesh(网格)是描述曲面的普遍方法。所以需要了解Mesh的几个操作Subdivision, simplification, regularization,也叫做 Geometry Processing(几何处理)。

3.Mesh Subdivision(Upsampling)

细分(Subdivision)包含两步,第一步是为了拆分出更多的三角形,第二步是使得它们的位置变化,从而让原来的模型变得更加光滑。

(1)Loop Subdivision(Triangle mesh)

网格细分算法有很多种,以Loop Subdivision(不是循环细分,只是因为发明者的famaily name为Loop)为例,它前提默认物体表面是三角形网格(否则不能使用),Loop Subdivision的第一步拆分是对于每个三角形,都连接其三条边的中点,这样就把1个三角形拆分为4个三角形,如下图:

接下类Loop subdivision的第二步位置改变是分别对新旧顶点进行位置改变,新顶点是指在上一步中新划分出的三个顶点(即上一步中的中点)。

对于新顶点而言,如下图中,在一般情况下白点为新顶点,由两个原来未拆分的三角形共享(暂时不考虑边界情况),将共享白点的边的两个顶点称为\(A,B\),非共享的称为\(C,D\)。则Loop细分对于新顶点的位置变化公式如下,也就是根据4点的位置进行更新。

\[\text{New vertex(Update to)}=\frac{3}{8}(A+B)+\frac{1}{8}(C+D) \]

对于旧顶点,如下图中白点为旧顶点,下图例子中的旧顶点由6个原来未拆分的三角形所共享,不一定非得是6个共享,也可以是其他数目,这里只是举例。

在这里定义两个量\(n,u\),其中\(n\)为顶点的度,\(u\)的取值规则如下:

\[\begin{aligned} n&=\text{vertex degree} \\ u&=\begin{cases} \frac{3}{16},& \text{if $n$ = 3}\\ \frac{3}{8n},& \text{otherwise} \end{cases} \end{aligned} \]

Loop细分对于旧顶点的位置更新公式如下:

\[\begin{cases} \text{Old vertex(Update to)}=(1-n*u)*\text{original_position}+u*\text{neignbor_position_sum} \\ \text{original_position}即为\text{Old vertex}原来的位置\\ \text{neignbor_position_sum}即为\text{Old vertex}与之相邻的旧顶点位置之和 \end{cases} \]

Loop细分结果图例子如下:

(2)Catmull-Clark Subdivision (General Mesh)

Catmull-Clark细分和Loop细分最大的不同就是可以作用于一般网格二不限于三角形网格(面)
首先它定义了如下概念:

  • quad face(四边形面)
  • Non-quad face(非四边形面)
  • Extraordinary vertex(奇异点,degree!=4)
    例子如下所示:

同样的Catmull-Clark细分也需要完成两步操作。具体步骤如下:
边都取其中点,对每个面(face)也取其中点,然后将边中点和面上中点连接,这样就增加了网格,然后对点和边的位置进行调整更新。

将上图经过一次细分之后如上图所示,从图上可以看到,增加了两个奇异点,并且增加的奇异点是在原本的非四边形面里的;换句话说一次细分之后每个非四边形面都会引入一个奇异点(即引入奇异点数目==非四边形面数目),并且引入奇异点之后所有的非四边形面都会消失,之后继续细分也不会再增加奇异点。

Catmull-Clark细分将点区分为3类来进行不同的位置调整更新,face/edge point,分别对应面/边中心的引入点,以及(Old)vertex point,也就是旧顶点。
对于face point,也就是引入新的点在面的中心时,如下图:

新顶点更新公式如下:

\[\begin{aligned} f&=\frac{v_1+v_2+v_3+v_4}{4}\\ e&=\frac{v_1+v_2+v_3+v_4}{4} \end{aligned} \]

旧顶点更新公式如下,其中\(p\)就是该点原本的位置,其余都是中点。

\[v=\frac{f_1+f_2+f_3+f_4+2(m_1+m_2+m_3+m_4)+4p}{16} \]

4.Mesh simplification(Downsampling)

对于有的情况不需要使用过于复杂的模型(不一定是因为计算能力的不足),就可以对网格进行简化。网格简化的方法有很多种,这里见到那介绍一下edge collapsing(边坍缩),找到不重要的边,然后将其合并到一起。

(1)Quadric Error Metrics(⼆次误差度量--权衡edge collapsing中边的重要性)

在edge collapsing中边的重要性中如何是一个问题,这里使用Quadric Error Metrics来解决这个问题。

以上图为例, 现在需要对左边的网格进行简化,使得在简化下方蓝色三角形网格能保持整个网格的大致轮廓(去除上面三个点)。二次误差度量就是找到某点,使其满足该点到其相关联的面的距离的平方和最小

(2)Edge collapsing(边坍缩)

以下图为例子,通过二次度量误差就可以完成边坍缩。

其算法步骤如下:

  1. 计算所有边的二次误差度量值
  2. 取最小误差值的边进行坍缩
  3. 更新二次误差度量
  4. 重复上述操作,直到结束

需要注意的是在对一条边进行坍缩之后,如上图所示,与其相连的其他边也发生了变化(跟随移动),这就需要重新更新其他边的二次度量误差。所以使用优先队列/堆来维护所有边的二次度量误差值,即允许常数时间内取出最小值且可以进行动态更新。

并且很明显上述算法是个贪心,贪心只能保证局部最优解,不能保证全局最优解,但是实际应用中证明两者结果大相径庭并不容易,所以仍然使用局部最优解。

posted @ 2020-07-27 21:53  Chasssser  阅读(637)  评论(0编辑  收藏  举报