计算三角网格的SDF的最有效方法

10

三角形,边缘和顶点生成的SDF

你好。

在过去的一个月里,我一直在不断搜集各种信息,但没有找到适合解决我的问题的想法。以下是问题的表述:

给定一个以缓冲几何体(32位顶点坐标和顶点索引的数组+附加数组,如顶点法线、UV或切线)的形式呈现的网格,在围绕几何体的均匀网格点上计算带符号距离函数(signed distance function,SDF)。

更具体地说,我想在3D引擎(如Maxon的Cinema4D或Blender)中创建类似于MetaBall对象的东西。我已经为所有几何图元实现了距离函数,但对于任意网格SDF,我需要实现一种暴力方法 - 为每个网格点测试每个三角形的距离 - 这当然会对复杂的网格产生很大的开销。

现在,我想起了大多数这些问题都需要构建类似于Octree、KD-tree、BSP-tree或AABB-tree的树状结构。然后我找到了一些关于所谓的快速扫描算法(用于解决Eikonal方程)的文章,该算法要求首先将边界上的网格点(在我的情况下是网格或最靠近网格的点)填充为0,其余点填充大值(无穷大),然后迭代地解决非线性双曲边值问题(Gauss-Seidel)。我还在CGAL库中发现了一个开源实现网格SDF方法的方法。或者,我也考虑使用一些着色器库(如GLSL),并尝试使用GPU构建树形结构,但我从未在JS或TS项目中使用过着色器。

我遇到的难题不仅仅在于选择最佳选项,而且在于实际高效地使用至少其中一种方法。例如:

  • 如果我想实现Fast-Marching Method,我必须遍历所有三角形,并为每个三角形遍历所有网格点Gijk。使用类似于Marching Cubes查找表的东西来处理网格单元格交叉点(但选项更多),我会对相交的单元格顶点插值接近0的值。我有一种感觉,这将花费很长时间,并且无法适用于实时更新。

  • 我成功找到了Unity中Ray Marching SDF计算的一些示例。由于我从未尝试直接在GPU上进行计算,因此我不知道例如并行计算的限制实际上是什么,也不知道这样的计算是如何执行的。我能够并行计算至每个三角形的距离,然后快速排序每个网格点Gijk的所有距离吗?如果可以,我要如何将其包含到TypeScript项目中?

  • 假设我在网格周围建立一个AABB-tree(应该是O(n * log(n))),然后给定一个网格点Gijk(假设根节点是包含Gijk和所有三角形的框),我将搜索最接近的叶子并计算所包含三角形的欧几里得距离(如果有多个,则选择其中一个)。 (请纠正我是否错误理解了)搜索应为O(log(n)),如果用户移动网格,则更新将需要O(n),其中n为三角形数量。因此,如果m是网格大小,则整个SDF计算将为O(m * n * log(n))?这看起来不像是对O(n * m)蛮力方法的改进,但也许我错了。

我考虑过结合多种方法,但这一切都似乎非常耗时。我还考虑过使用CGAL库或为我的项目重新设计它,但由于库内的所有依赖性,我发现很难理解C ++代码。您中是否有人做过类似的事情?您会推荐什么?

感谢您提供的所有见解。


你只需要构建一次AABB树,时间复杂度为O(n * log(n))。如果你为静态网格构建SDF结构,则复杂度为O(m * log(n)),因为你只需要进行查询。如果网格被编辑,则整个结构必须重新计算,但这是另一回事了。这方面有任何进展吗? - R.Falque
1个回答

3
我使用了一个包含三(四)个主要步骤的方法来解决这个问题:
  1. 生成网格三角形Soup的AABB树

  2. 使用AABB树将常规边界立方体细分,每当一个立方体与网格相交时(使用AABB树快速相交),形成一个八叉树。当细分达到叶子时,计算精确的平方距离(立方体重心到三角形)并取最小值的平方根,将其写入基于立方体最小-最大坐标的常规网格中。

  3. 在网格相交的体素上使用精确的距离值和大的值,在其他地方运行8次快速扫描算法,并用距离值填充标量网格。

  4. (可选)对负距离网格的外部体素进行洪水填充,以确保标记的初始体素轮廓内的体素(具有精确值)具有负符号(不幸的是,对于分辨率> 100 ^ 3的网格,这是最慢的部分)

如果您想详细了解我的实现及计算时间,欢迎阅读我的博客文章: https://mshgrid.com/blog/ 感谢您的点赞和评论 :)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接