网格生成之marching cube算法学习笔记

转载地址:https://www.jianshu.com/p/5a6ade7b77b6

前言
在学习网格生成算法的计划中,建议大家先了解Marching Cube(MC),为什么呢,他也不是一个端到端的网格生成算法?因为很多连续算法在最后提取等值面的时候都会采用marching cube或其改进版本,可以说是很多算法的最后一步。如果不理解这一步怎么做的,也很难理解其他算法之前的那么多计算的目的是什么。除此之外可以大大简化其他算法的解释过程,最后一步,就可以忽略不解释了。所以我们大家开始吧。

提前了解
在前言中,我们提到了一个名词,叫做等值面,这是一个很重要的概念,在解释MC算法前,我们先来解释一下等值面。

等值面是什么?
首先等值面是空间中的一个曲面,我们规定空间中每一个点都有一个属性值,属性值相等连续空间组成的曲面,我们称之为等值面。可能还是不太直接,跟我们网格生成有什么关系,我们接下来看。

我们在采集深度点云时,由于传感器自身精度以及配准的误差在一个地方会采集到很多点,可以认为是最空间连续点的采样,而且点的位置会有很大的波动。我们通过一些其他方法可以得出这些点与真正的面的差值,
在这里插入图片描述
那么真正的面在哪里呢?
假如一个点在面的前方,我们标记为+1,一个点在真实面的后方,标记为-1,我们可以认为这个真正的面就在这两个点的中间。

我们还可以这么认为,就是空间内真正的面,会产生一个场,场内有无数个等值面,这个值我们暂且称之为空间场值,空间场值在面的前方为正值,后方为负值。离真实面越近,空间场值的绝对值越小,真实面的空间场值为0(暂定为0,也可以是其他的)。我们的目的就是在对这个场进行采样后,找到空间内真正的面,数值怎么获得我们这篇文章暂不关注。

Marching Cube(MC)算法简介
我们大概介绍了等值面,那么怎么提取等值面呢?这就是Marching Cube算法的功能以及目的。
MC是 W.Lorensen等人于1987年提出来的一种体素级重建方法。MC算法也被称为“等值面提取”(Iso surface Extraction)算法。

下面我们一起开看看Marching Cube的算法思路,可能会对理解更有帮助,前提记住Marching Cube是为了提取空间中的等值面,并用三角面近似表示出来。

Marching Cube 算法思路
Marching Cube 首先将空间分成众多的六面体网格,类似将空间分成很多的小块
在这里插入图片描述

然后找到等值面经过的六面体网格,如下图:
在这里插入图片描述

求出该六面体的边与0等值面(0等值面就是空间场值为0的等值面)的交点,将这些交点按照一定的拓扑连接关系连接起来,作为0等值面在该六面体网格中的近似表示。注意是近似表示,我们采用三角形。
简单的介绍完,可能还有很多的不明白的地方,我们罗列出来一个一个的解释:
1、如何找到0等值面经过的六面体网格?
2、怎么求六面体边与0等值面的交点?
3、这些交点按照怎么的拓扑连接关系连接,是怎么操作的?

1. 如何找到等值面经过的六面体网格?
前面我们提到过,我们有很多的已知采样点,并且知道这些点在空间中的空间场值,现在我们将空间分为很多个小格子,每个小格子都有8个顶点,我们通过计算8个顶点周围(范围与六面体的大小相关)的采样点,近似计算出8个顶点的空间场值(加权评价等方法)。我们就可以对六面体做以下分类,我们分析一下以下情况:
a) 八个顶点都没有空间场值,0等值面不经过或者经过但是没有采样,我们没有办法进行近似,所以不处理。
b) 八个顶点都有或部分有空间场值,当空间场值都是正值或者都是负值时,说明0等值面,不在六面体内。
当六面体内的的顶点的空间场值有正有负时,则说明有0等直面穿过六面体。我们需要从众多六面体内,筛选出这样的六面体。

2. 怎么求六面体边与0等值面的交点?
因为六面体有8个顶点,8个顶点的状态就有2^8 = 256中分布状态,但是由于六面体是有对称性的,我们通过对称性,可以简化这些状态至15种状态,至于怎么简化,为什么相等,大家简单想一想就好,这个并不复杂。我们看一下15中状态:
在这里插入图片描述

我们上面展示了等值面与六面体相交的情况一共就这样15种,其中还有一个没有交点的,也可以说14种。现在我们就需要精算出来,这个交点的具体的位置,下面我们举一个例子:
我们先给六面体的所有的顶点和边坐上标记:
在这里插入图片描述

假如体素下方的顶点3的值小于等值面值,其他顶点上的值都大于等值面值,那么我们可以生成一个与体素边2,3,11相交的三角面片,而三角面片顶点的具体位置则需要根据等值面值和边顶点3-2,3-0,3-7的值线性插值计算得到。
在这里插入图片描述

我们将交点坐标用P表示,P1、P2代表边上两个端点的坐标,V1、V2代表这两个端点上的值,V代表等值面值,那么交点坐标的计算公式如下(就是简单的线性差值,当然还有很多种方法,先简单介绍一个简单的线性插值):

P = P1 + (V – V1)·(P2 – P1)/(V2 – V1)

那么现在我们对所有的含有等值面的六面体计算出了其与等值面的角点。下一步也就是最后一步,就是将这些角点链接成三角形就好了。我们来看看怎么做的。

3. 这些交点按照怎么的拓扑连接关系连接,是怎么操作的?
其实这些拓扑结构,也在我们上图15中情况中,都花了出来,只要按照上图的表中已经记录好的连接关系,将三角形连接好就行了。这有点像废话,其实第一张连接关系的表不是计算机生成的,而是在较早的时候有人手动统计的,他考察了所有256种情况的体元配置并画出其中的三角形,这是何等的耐心。那通过算法该怎么生成呢?不知道Leetcode里面是否有这样的考题,(@ο@) ~

我们现在应该基本上理解了算法的思路,下面我们整理一下算法的流程。

Marching Cube 算法流程
1、将原始数据经过预处理之后,读入特定的数组中或者八叉树。
2、从网格数据体中提取一个六面体,成为当前六面体"同时获取该六面体的所有信息,例如8个顶点的值,坐标位置等。
3、将当前六面体8个顶点的函数值与给定等值面值C进行比较,得到该六面体的状态表。
4、根据当前六面体的状态表索引,找出与等值面相交的六面体棱边,并采用线性插值的方法,计算出各个交点的位置坐标。
5、利用中心差分法,求出当前六面体8个顶点的法向量,在采用线性插值的方法,得到三角面片各个顶点的法向。
6、根据各个三角面片顶点的坐标,顶点法向量进行三角面的连接。

Marching Cube的问题
当然最初的Marching Cube 有很多问题,例如拓扑连接二义性,一种状态可以有多种连接关系
在这里插入图片描述

而且Marching Cube的效率不是特别高,需要借助分层结构和并行计算。
而且Marching Cube生成面片的大小与六面体的大小相关,过大则会导致模型模糊,细节消失,过小会导致面片的数目过多。

但是从1987年提出Marching Cube至今,已经对其有了很多的改进和优化算法,在处理时间,减少内存开销,分辨率方面都有很大的优化。我们再在其他文章中进行介绍。

总结
Marching Cube 作为提取等值面的最基本的方法,可以说是在研究网格生成里面最基础的算法,也是不可不知的算法。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在UE4中实现Marching Cubes算法,需要完成以下步骤: 1. 创建一个场景,并在场景中创建一个Actor用于实现Marching Cubes算法。 2. 定义需要生成的体数据,包括体的大小、分辨率、采样间隔、数据类型等参数。 3. 在Actor的BeginPlay()函数中,生成体数据并存储到一个三维数组中。 4. 实现Marching Cubes算法,将体数据转换为网格数据。这可以通过使用UE4中的Procedural Mesh Component实现,Procedural Mesh Component是UE4中提供的一个用于程序化生成网格的组件。 5. 将生成网格数据设置到Procedural Mesh Component中,以在场景中显示出生成网格。 下面是一个简单的实现Marching Cubes算法的示例代码: ```cpp // 定义需要生成的体数据 int32 SizeX = 32; int32 SizeY = 32; int32 SizeZ = 32; float SampleSpacing = 0.5f; TArray<float> VolumeData; VolumeData.Init(0.f, SizeX * SizeY * SizeZ); // 在Actor的BeginPlay()函数中生成体数据 void AMyActor::BeginPlay() { Super::BeginPlay(); GenerateVolumeData(); GenerateMesh(); } // 实现Marching Cubes算法 void AMyActor::GenerateMesh() { // 创建Procedural Mesh Component UProceduralMeshComponent* ProceduralMesh = NewObject<UProceduralMeshComponent>(this); ProceduralMesh->RegisterComponent(); ProceduralMesh->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); // 生成网格数据 TArray<FVector> Vertices; TArray<int32> Triangles; TArray<FVector> Normals; TArray<FLinearColor> VertexColors; TArray<FVector2D> UVs; MarchingCubes(VolumeData, SizeX, SizeY, SizeZ, SampleSpacing, Vertices, Triangles, Normals, VertexColors, UVs); // 设置网格数据到Procedural Mesh Component中 ProceduralMesh->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UVs, VertexColors, TArray<FProcMeshTangent>(), true); } // Marching Cubes算法的实现 void AMyActor::MarchingCubes(const TArray<float>& VolumeData, int32 SizeX, int32 SizeY, int32 SizeZ, float SampleSpacing, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FLinearColor>& VertexColors, TArray<FVector2D>& UVs) { // TODO: 实现Marching Cubes算法 } ``` 需要注意的是,Marching Cubes算法的实现需要一定的数学基础,涉及到插值、梯度计算、多边形拓扑等。因此,具体的实现需要进行一定的编程和数学实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值