将 SDF 计算为三角形网格的最有效方法
Posted
技术标签:
【中文标题】将 SDF 计算为三角形网格的最有效方法【英文标题】:Most Efficient Way to Calculate an SDF to a Triangle Mesh 【发布时间】:2020-01-25 18:51:49 【问题描述】:嗨。
在过去的一个月里,我一直在不断地从各种来源收集信息,但没有找到适合我特定问题的想法。所以这是问题的表述:
给定一个缓冲区几何形式的网格(顶点坐标和顶点索引的 32 位数组 + 其他数组,例如顶点法线、uvs 或切线)计算一个有符号距离函数 (SDF)几何图形周围点的统一网格。
更具体地说,我打算在 Maxon 的 Cinema4D 或 Blender 等 3D 引擎中创建类似于 MetaBall 对象的东西。我已经成功地为所有几何图元实现了距离函数,但是任意网格 SDF 要求我实现蛮力方法 - 为每个网格点测试网格的每个三角形的距离 - 当然,对于复杂的网格。
现在,我回忆起这些问题中的大多数都需要构建树状结构,例如八叉树、KD-tree、BSP-tree 或 AABB-tree。然后我发现了一些关于所谓的Fast-Sweeping algorithm(用于求解 Eikonal 方程)的文章,它需要首先用 0 和剩余的具有较大的值(Infinity),然后迭代求解非线性双曲边值问题(Gauss-Seidel)。我还在CGAL library 中找到了网格 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) ),因为您只是进行查询。如果编辑了网格,则必须重新计算整个结构,但那是另一回事了。这方面有什么进展吗? 【参考方案1】:我使用三(四)个主要步骤概述的方法解决了这个问题:
生成网格三角形汤的AABB树
只要立方体与网格相交(使用 AABB 树的快速相交)形成八叉树,就使用 AABB 树细分常规边界立方体。当细分到达叶子时,计算精确的平方距离(立方体质心到三角形)并取最小的平方根,然后将其写入基于立方体最小-最大坐标的规则网格。
在网格相交体素上使用精确的距离值并在其他地方使用较大的值,运行快速扫描算法 8 次并用距离值填充标量网格。
(可选)洪水填充否定距离网格的外部体素,以确保标记的初始体素(具有精确值)轮廓内的体素具有负号(不幸的是,这是分辨率 > 100 的网格中最慢的部分^3)
如需更详细地了解我的计算时间实现,请随时阅读我的博客文章: https://mshgrid.com/blog/
感谢您的支持和 cmets :)。
【讨论】:
以上是关于将 SDF 计算为三角形网格的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章