计算机图形学:几何表示

一、引言

在计算机图形学中,我们经常需要用数学方法描述一个物体的形状。例如,一个球体、一张曲面,或者一个复杂的三维模型,都可以通过不同的数学方式进行表示。

那么,如何用数学语言描述一个物体?

一种直观的想法是,直接记录物体表面上所有点的坐标。但对于复杂的三维物体来说,表面上的点可能非常多,直接存储所有点不仅占用空间,而且难以精确描述连续的曲面。

另一种思路是,不直接记录表面上的所有点,而是定义一个数学公式,通过这个公式来描述物体的形状。

根据描述物体的方式不同,我们可以将几何表示大致分为三类:

  • 隐式表示(Implicit Representation):通过数学方程或函数描述物体。

  • 参数化表示(Parametric Representation):通过参数和函数,将参数空间中的点映射到物体表面。

  • 显式表示(Explicit Representation):直接存储或构造物体的几何元素,例如点、曲线和网格。

这三种方法各有优缺点,适用于不同的场景。

二、隐式表示:用公式定义物体

2.1 什么是隐式表示?

隐式表示的核心思想是:不直接描述物体表面上的每一个点,而是定义一个函数,让满足特定条件的空间点构成物体表面。

以三维空间中的球体为例。

假设一个球体的球心位于原点,半径为 (r),那么它的表面可以用以下方程描述:

x²+y²+z²=r²

这个方程并没有直接列出球面上有哪些点,而是规定了球面上所有点必须满足的条件。

例如,当 (r=1) 时:

x²+y²+z²=1

所有满足该方程的三维空间点连在一起,就构成了一个单位球的表面。

我们还可以进一步定义一个隐式函数:

f(x,y,z)= x²+y²+z²

通过判断函数值,可以区分点与球体之间的关系:

  • (f(x,y,z)=0):点位于球体表面。

  • (f(x,y,z)<0):点位于球体内部。

  • (f(x,y,z)>0):点位于球体外部。

这就是隐式表示的一个重要优势:判断一个点在物体内部、外部还是表面上,往往非常方便。

如果需要知道物体的点的具体位置,就会变的很麻烦了。

2.2 隐式表示的典型例子

1. CSG:构造实体几何

CSG(Constructive Solid Geometry,构造实体几何)是一种通过组合基本几何体来构造复杂物体的方法。

例如,我们可以使用一个立方体和一个球体构造新的物体,常见操作包括:

  • 并集:将两个物体合并。

  • 交集:保留两个物体共同占据的部分。

  • 差集:从一个物体中减去另一个物体。

通过并集,交集,差集也就可以使用规律的几何物体来表示出不规则的物体。

2 SDF:有符号距离函数

SDF 的全称是 Signed Distance Function,即有符号距离函数。

它不仅可以判断点在物体内部还是外部,还可以描述点到物体表面的距离。

对于一个球体,可以定义:

d(x,y,z)=sqrt{x^2+y^2+z^2}-r

其中:

  • d = 0:点在球面上。

  • d < 0:点在球体内部。

  • d > 0:点在球体外部。

对于球体而言,这个函数的绝对值就是点到球面的最短距离。

SDF 常用于实时渲染、光线步进、三维建模和几何融合等领域。

在几何融合中,SDF是一个非常重要的函数。

几何融合步骤:

第1步:定义两个物体的符号距离函数(SDF)

首先,分别定义物体 A 和物体 B 的符号距离函数。

  • dA(p):表示空间中的任意点 p 到物体 A 表面的符号距离。

  • dB(p):表示空间中的任意点 p 到物体 B 表面的符号距离。

这里的 SDF 是 Signed Distance Function 的缩写,中文称为符号距离函数。

通常情况下:

  • 距离值小于 0,表示点在物体内部。

  • 距离值等于 0,表示点位于物体表面。

  • 距离值大于 0,表示点在物体外部。

例如,假设物体 A 是左侧的球体,物体 B 是右侧的球体。对于空间中的任意一个点 p,我们可以分别计算它相对于两个球体表面的符号距离,得到 dA(p) 和 dB(p)。

第2步:使用平滑融合函数(Smooth Union)

得到两个物体的 SDF 后,就可以通过布尔运算将它们合并。

普通并集:直接取最小值

计算公式:

dHard = min(dA, dB)

其中,min 表示取两个数中较小的值。

这种方法可以将两个物体合并成一个整体,但连接区域可能出现明显的拼接痕迹,缺少平滑过渡效果。

平滑并集:使用 Smooth Min 函数

计算公式:

dSmooth = smooth_min(dA, dB, k)

其中:

  • dA:当前点到物体 A 的符号距离。

  • dB:当前点到物体 B 的符号距离。

  • k:平滑融合参数,用来控制融合区域的宽度。

  • dSmooth:融合之后得到的符号距离值。

这里的 smooth_min 表示平滑最小值函数。与直接取最小值不同,它会在两个物体距离接近的区域,对距离值进行平滑混合,从而形成自然的过渡曲面。

参数 k 控制融合效果。通常来说,k 越大,平滑过渡区域越宽,两个物体看起来就越像橡皮泥一样自然地连接在一起。

需要注意,具体的融合效果还取决于所使用的 smooth_min 函数实现。

第3步:得到融合之后的整体 SDF 场

经过平滑并集运算后,整个三维空间中的每一个点都可以获得一个新的符号距离值(SDF),记作 dSmooth。

这个距离场描述了融合后物体的整体形状。

判断点的位置时,可以根据距离值的正负进行区分:

  • dSmooth 小于 0:点位于融合物体内部。

  • dSmooth 等于 0:点位于融合物体的表面,也就是需要渲染的曲面。

  • dSmooth 大于 0:点位于融合物体外部。

因此,只需要找到整个三维空间中距离值等于 0 的位置,就可以提取融合后物体的表面,并进一步进行三维可视化或渲染。

三、参数化表示(Parametric Representation)

3.1 基本思想

参数化表示的核心思想是:通过参数和函数,直接生成物体表面上的点。

对于三维曲面,可以使用两个参数 u 和 v,定义三个坐标函数:

x=x(u,v)

y=y(u,v)

z=z(u,v)

因此,曲面可以表示为:

P(u,v)=(x(u,v),y(u,v),z(u,v))

通过函数将(u,v)和坐标(x,y,z)建立联系,只要给定参数 u 和 v,就可以计算出曲面上对应点的三维坐标。

当 u 和 v 在指定范围内变化时,生成的所有点就构成了一个曲面。

3.2 示例:马鞍面

例如,定义一个参数化曲面:

x=u

y=v

z=u^2-v^2

合起来就是:

P(u,v)=(u,v,u^2-v^2)

当 u 和 v 变化时,就会生成一个马鞍形曲面,也就是双曲抛物面。

这种曲面在一个方向向上弯曲,在另一个方向向下弯曲。

参数化表示的优势在于,我们可以通过调整参数范围、改变映射函数或采样参数空间,来生成和控制曲面的形状。

四、显式表示(Explicit Representation)

显式表示是指直接存储或构造几何元素来描述物体。

常见的例子包括点云、三角形网格、OBJ 文件和贝塞尔曲线。

4.1 点云(Point Cloud)

点云由大量离散的三维点构成。

每个点通常包含三个坐标,分别表示它在三维空间中的 x、y、z 位置。

例如,第 i 个点可以表示为:

pi = (xi, yi, zi)

整个点云可以表示为:

P = {p1, p2, ..., pn}

其中,n 表示点云中包含的点的总数,pi 表示第 i 个三维空间点。

例如,通过三维扫描设备扫描一个物体,就可以得到大量分布在物体表面附近的点。

这些点共同描述了物体的形状。

点云的优点是结构简单,能够直接记录采样数据。

但是,点云通常不包含明确的点与点之间的连接关系,因此它本身不一定构成完整的连续曲面。

4.2 OBJ 文件

OBJ 是一种常见的三维模型文件格式。

它可以存储:

  • 顶点坐标。

  • 纹理坐标。

  • 顶点法线。

  • 多边形面的顶点索引。

点云只记录点的位置,而三角形网格还会记录顶点之间的连接关系。

因此,网格能够更明确地描述物体表面的结构,方便渲染、纹理映射和几何编辑。

需要注意,OBJ 是一种文件格式,而不是一种独立的数学表示理论。

五、贝塞尔曲线(Bézier Curve)

5.1 基本思想

贝塞尔曲线是一种通过少量控制点来控制曲线形状的数学方法。

它广泛应用于计算机图形学、字体设计、矢量绘图和动画等领域。

贝塞尔曲线不需要直接存储曲线上的所有点,而是通过控制点和参数计算生成曲线。

5.2、贝塞尔曲线的基本性质

贝塞尔曲线不仅可以通过控制点灵活地控制曲线形状,还具有一些重要的数学性质。这些性质使它在计算机图形学、字体设计、矢量绘图和动画轨迹生成等领域得到了广泛应用。

1、端点插值性质

贝塞尔曲线一定经过起点和终点。

对于三次贝塞尔曲线,其起点和终点分别为:

起点:B(0) = P0

终点:B(1) = P3

其中,P0、P1、P2、P3 是三次贝塞尔曲线的四个控制点。

这说明,贝塞尔曲线的第一个控制点决定曲线的起点,最后一个控制点决定曲线的终点。

需要注意的是,中间的控制点 P1 和 P2 通常不在曲线上,而是用于控制曲线的形状和弯曲方向。

2、切线性质

贝塞尔曲线在起点和终点处的切线方向,可以通过相邻控制点的位置关系确定。

对于三次贝塞尔曲线:

起点处的导数:

B'(0) = 3 × (P1 - P0)

终点处的导数:

B'(1) = 3 × (P3 - P2)

从这两个公式可以看出:

  • 起点处的切线方向由 P0 指向 P1 的方向决定。

  • 终点处的切线方向由 P2 指向 P3 的方向决定。

因此,如果希望改变曲线起点附近的方向,可以调整 P1 的位置;如果希望改变曲线终点附近的方向,可以调整 P2 的位置。

这也是三次贝塞尔曲线能够灵活控制曲线形状的重要原因。

3、仿射变换性质

贝塞尔曲线具有仿射变换性质。

简单来说,如果对贝塞尔曲线的所有控制点进行相同的仿射变换,那么得到的新曲线,就等于对原来的曲线进行相同的变换。

常见的变换包括:

  • 平移:改变曲线在空间中的位置。

  • 旋转:改变曲线的朝向。

  • 缩放:改变曲线的大小。

  • 剪切:改变曲线的倾斜形状。

例如,将三次贝塞尔曲线的四个控制点全部向右移动一定距离,生成的曲线也会整体向右移动相同的距离。

因此,在图形变换过程中,我们不需要重新计算曲线的每一个点,只需要对控制点进行相应的变换即可。

5.3、二次贝塞尔曲线

二次贝塞尔曲线由三个控制点构成:

  • P(0):起点。

  • P(1):中间控制点。

  • P(2):终点。

  • 其计算公式可以用普通文本表示为:

需要取一个时间 t ,这个 t 就表示曲线的 "进度条" ,可以通过进度条,用来取出曲线上面的一个点。

对于二次贝塞尔曲线来说,根据 t 在线段P0,P1上面进行插值求出插值点,在P1,P2同理求出插值点,在两个求出插值点上面再次进行插值,求出在 t 时刻的贝塞尔曲线的点。

最终可以得出下面公式:

B(t) = (1 - t)² × P(0) + 2 × (1 - t) × t × P(1) + t² × P(2)

其中,P(1) 用来控制曲线的弯曲程度和方向。

当中间控制点的位置发生变化时,曲线的形状也会随之改变。

二次贝塞尔曲线结构简单,适合描述较为简单的弧形。

5.3 三次贝塞尔曲线

三次贝塞尔曲线由四个控制点构成:

  • P(0):起点。

  • P(1):控制起点附近的曲线方向。

  • P(2):控制终点附近的曲线方向。

  • P(3):终点。

其计算公式可以用普通文本表示为:

B(t) = (1 - t)³ × P(0) + 3 × (1 - t)² × t × P(1) + 3 × (1 - t) × t² × P(2) + t³ × P(3)

与二次贝塞尔曲线相比,三次贝塞尔曲线多了一个控制点,因此可以更加灵活地调整曲线的形状。

在计算机图形学中,三次贝塞尔曲线应用非常广泛,例如字体轮廓、矢量图形、路径绘制和动画运动轨迹等。

5.4 为什么要使用分段贝塞尔曲线?

假设我们有很多数据点,希望曲线经过这些点。

如果直接把这些点当作一条贝塞尔曲线的控制点,曲线通常不会经过所有中间控制点。

而且,当控制点数量过多时,曲线的整体形状也会变得难以控制。

因此,实际应用中经常采用分段贝塞尔曲线。

例如,使用多段三次贝塞尔曲线连接一系列指定点,每段曲线都使用四个控制点。

这样既可以让曲线经过指定的连接点,又可以分别调整不同曲线段的形状。

但是,仅仅让相邻曲线段的端点重合,并不意味着曲线一定平滑。我们还需要考虑曲线的连续性。

六、贝塞尔曲线的连续性

当多段贝塞尔曲线连接在一起时,需要考虑连接处是否平滑。常见的连续性条件包括 C0 连续、C1 连续和 G1 连续。

它们的区别主要在于:连接处的位置是否重合,以及两侧曲线的切线方向和变化速度是否一致。

6.1 C0 连续:位置连续

C0 连续要求相邻曲线段的连接点重合。

假设第一段曲线为 B1,第二段曲线为 B2,那么需要满足:

B1(1) = B2(0)

其中,B1(1) 表示第一段曲线的终点,B2(0) 表示第二段曲线的起点。

这个条件说明,第一段曲线的终点必须与第二段曲线的起点处于同一个位置。

C0 连续只能保证曲线不会断开,但不能保证连接处没有尖角。因此,两段曲线虽然连接在一起,仍然可能看起来不够平滑。

6.2 C1 连续:一阶导数连续

C1 连续不仅要求相邻曲线段的位置连续,还要求连接处的一阶导数(切线的方向和大小)相同。

需要满足以下两个条件:

位置连续:

B1(1) = B2(0)

一阶导数连续:

B1'(1) = B2'(0)

这里的 B1'(1) 和 B2'(0) 分别表示第一段曲线终点处、第二段曲线起点处的导数。

可以将导数理解为曲线在某个位置的切向量,它能够反映曲线在该位置的切线方向和变化速度。

为了使两段曲线满足 C1 连续,需要满足以下条件:

第一,连接点重合:

P3 = Q0

第二,连接处的切向量(切线的方向和大小)相同:

P3 - P2 = Q1 - Q0

这意味着连接处两侧的切线方向相同,并且切向量的大小也相同。

因此,C1 连续能够让曲线在连接处具有更加平滑的过渡效果。

七、曲面细分

1、什么是曲面细分?

在计算机图形学中,曲面细分(Subdivision Surface)是一种通过不断细分多边形网格,并按照特定规则调整顶点位置,使模型逐渐变得平滑的技术。

它的基本思想是:

  1. 拓扑细分:在原有网格的边或面上生成新顶点,并建立新的边和面。

  2. 几何调整:根据细分规则计算新顶点的位置,同时更新旧顶点的位置。

  3. 重复迭代:对细分后的网格再次执行上述过程,使模型逐渐逼近平滑曲面。

因此,曲面细分并不只是简单地增加三角形数量。若只把每个三角形切成四个更小的三角形,而不调整顶点位置,模型的表面通常仍然保持原来的形状。

真正让模型变得平滑的是顶点位置的重新计算。

2、Loop 细分:针对三角形网格

Loop Subdivision 是一种经典的三角形网格细分方法。

它的特点是:每个原始三角形被拆分成四个新的三角形,同时重新计算边上的新顶点和原始顶点的位置。

对于生成的点来说:

内部边的两个端点为 A、B,相邻三角形的另外两个顶点为 C、D:

复制代码
vec3 edgePoint = 3.0 / 8.0 * (A + B)
               + 1.0 / 8.0 * (C + D);

生成边上新顶点之后,原来1个三角形就被切为了4个小三角形了。

对于本来存在的点来说:

图上面的白点为例,旧顶点的变化公式为:

n :顶点的度,即与该顶点相连的边的数量

u :权重系数

n = 3 :u = 3/16

其他情况:u = 3/(8*n)

1 - n*u :旧顶点自身保留的权重

P :旧顶点的坐标

P_neighbors:所有直接相邻顶点的坐标

所以Loop细分也就是生成新顶点,调整旧顶点,重构三角形。

四、Catmull--Clark 细分

对于三角形和四边形都存在的网格,经典的细分方法是 Catmull--Clark Subdivision。

与 Loop 不同,Catmull--Clark 在每一轮细分中,会为每个原始面生成一个面点,为每条原始边生成一个边点,并更新每个原始顶点。

这里可能有点疑问:就是在光栅化之后,所有的网格都会变成了三角形,怎么还会有四边形的网格呢,对于网格细分的操作是在顶点着色器之后,光栅化之后实现的,在OpenGL里面也提供了一个glsl叫做几何着色器可以对视角转化后的顶点进行操作。

  • Ordinary Vertex(普通顶点):顶点度(Valence)等于 4,即有四条边与该顶点相连。这是规则四边形网格中典型的内部顶点。

  • Extraordinary Vertex(奇异顶点):顶点度不等于 4,即与该顶点相连的边数不是 4。图中原始网格的 5 个紫色圆点就是奇异顶点。

经过变化后的图形如下:

可以看到新增了一个面点,两个边点,和两个奇异点。

面点的计算:

计算方法:将该面所有顶点的坐标相加,再除以顶点数量。

公式:

边点的计算:

旧顶点的计算:

原始顶点也需要更新位置,而不是简单地保留在原处。

奇异顶点的数量不变,度数不变(还是原来那5个紫色点,没有新增奇异点),顶点和面片数量暴涨,网格变密,模型表面变得平滑。

Catmull-Clark 一轮细分可以记成一句话:先算面点,再算边点,最后更新旧顶点。

八、边坍缩

一、什么是边坍缩

边坍缩(Edge Collapse)是一种常见的三角形网格简化算法。

它的核心思想是:将一条边的两个端点合并为一个新顶点,同时删除退化的三角形,并更新周围网格的连接关系,从而减少模型的顶点数、边数和三角形数量。

二、二次误差度量

对于边的坍塌,不能随意选择,所以使用到二次误差度量。

也就是找到一个点,使得这个点与要坍缩的两个顶点所连接的各个面的平方和距离最小。

每个平面都像一个约束,新顶点偏离这些平面越多,误差通常就越大。 二次误差度量通过最小化所有相关平面的距离平方和,寻找一个整体上最合适的新顶点位置。

具体过程可以去了解,最后可以得出一个浮点数,浮点数越小说明这条边二次误差度量后的影响越小,还可以顺便算出来另外一个东西,也就是最新顶点坐标。

所以接下来就可以将所有的边记录一下二次误差度量,之后排序,将误差少的优先进行二次误差度量,直到只用规定的边来表示模型。

这里同时会出现一个问题,就是计算出每一条边的二次误差度量后,在我们进行边坍缩之后,会影响这条边附近的边。

可以使用小根堆+版本号解决:

给每条边维护一个版本号。

cpp 复制代码
struct Edge {
    bool alive;
    int version;
    double cost;
};

将所有边的二次误差度量装入小根堆后,取出堆顶的边,更新边的顶点后,对边附近的其他会受影响的边重新计算二次误差度量,更新边的版本号,之后每一次取出边都与版本号比较,如果不一样,更改边的二次误差度量,重新插入小根堆中。

三、边坍缩的场景

对于游戏和实时渲染里面,很多时候,当照相机在远处的时候,模型是非常小的,也就不需要花费大量的面来构建模型,当照相机靠近的时候,根据照相机的距离来使用合适的面来构造模型。

一般来说,生成好的模型,在制作的时候就会提前用边坍缩的算法制作由不同面构成的模型,判断LOD来决定改用哪一套模型。

  • LOD0:最高精度,物体离相机很近时用

  • LOD1:面数减半,中等距离

  • LOD2:面数更少,更远

  • LOD3:极简模型,很远的时候渲染

每一级LOD模型,都是提前在软件里算好、导出。

相关推荐
啵啵啵鱼42 分钟前
第二章 线性表
java·数据结构·算法
局部有解1 小时前
Hot100 - 三数之和
数据结构·c++·算法
思茂信息1 小时前
CST技术案例:加载正弦曲线的平面螺旋天线仿真研究
算法·平面·emc·cst·电磁仿真·仿真辐射
嫂子开门我是_我哥1 小时前
敏感性分析
算法·机器学习·论文笔记
别动我齐刘海2 小时前
实时控制学习2
linux·c++·人工智能·学习·算法
有Li2 小时前
EEG脑电图数据处理基础
人工智能·算法·机器学习·语言模型·医学生
XMXFS2 小时前
小红书推荐算法底层逻辑解析:用数据思维做内容优化
python·算法·新媒体运营·推荐算法
0+1112 小时前
算法 --栈
算法·leetcode
鶴哥只手遮天2 小时前
Gmsh 源码深度解析-第 2 章 几何内核
算法·开源