在 Unity 中平滑动态生成的网格?

Posted

技术标签:

【中文标题】在 Unity 中平滑动态生成的网格?【英文标题】:Smooth dynamically generated mesh in Unity? 【发布时间】:2018-05-10 12:56:34 【问题描述】:

给定一个 Unity 和 C# 中的网格(它本身是通过合并更简单的基础网格实时创建的),我们如何在运行时*将它变成一个平滑的,几乎就像包裹在布料中的网格版本一样?不是完全凸出的版本,而是更圆润、柔化锋利边缘、弥合深间隙等。当“平滑角度”法线设置应用于导入的对象时,曲面的理想外观也是如此。谢谢!

草图前后

*网格设置是由人制作的,其细节事先未知。它的所有基本形状部分(在我们合并它们之前)都是已知的。如果这有助于解决方案,基础部分也可能保持未合并,如果有一个运行时解决方案可以快速应用包装器混搭,即使基础部分随时间改变其变换,但静态一次性转换也会很棒。

(一些相关的关键字可能是:marching cube algorithm & metaballs、skin above bone、meshfilter conversion、smoothing shader、softening、vertices subdivision。)

【问题讨论】:

你需要什么吗this 看起来很有趣,我试试看,谢谢! This approach 效果很好。 它看起来很完美。但是当我将此代码附加到一个对象时它会消失。我应该使用 Raymarching 的东西吗? 【参考方案1】:

有很多方法可以获得类似的东西,因此您可以选择自己喜欢的:

行进方块

这个算法很容易使用,但结果总是继承了它的块状“风格”。如果那是您想要的外观,请使用它。如果您需要更平滑和/或像素完美的东西,请寻找其他方法。

Ray Marching 和有符号距离函数

这是一种非常有趣的技术,可以让您进行大量控制。您可以用简单的立方体/圆柱体/等来表示您的基础部件。方程式并将它们与简单的数学混合在一起。

这里你可以看到一些例子: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm

这里最好的一点是设置非常简单,您甚至不需要合并基础部分,只需将数据推送到渲染器即可。更糟糕的是,它可能会在渲染部分变得难以计算。

老式网格修改

这里有最多的选择,但也最复杂。您从自己没有太多数据的基础部分开始,因此您可能应该使用 CSG 联合操作将它们连接到一个网格中。

有了这个网格,你就可以为你的图元计算邻居数据:

为每个顶点找到包含它的三角形。 为每个顶点找到包含它的边。 为每条边找到包含它的三角形。

等等

有了这些数据,您或许可以做以下事情:

找到并切割一些尖锐的顶点。 找到并切割一些锋利的边缘。 移动顶点以最小化它创建的三角形/边之间的角度。

等等……

确实有很多细节可能对你有用,你只需要测试一些,看看哪一个给出了首选的结果 .

我会从一件简单的事情开始:

对于每个顶点,找到通过任何边连接到它的所有顶点。 计算所有这些顶点的平均位置。 使用 [0,1] 范围内的一些 alpha 参数在初始顶点位置和平均位置之间进行混合。 实现此算法的多次迭代并为其添加参数。 使用 alpha 和迭代次数进行实验。

使用这种方式,您还有两个不同的阶段:计算和渲染,因此使用动画可能会变得太慢,但仅渲染网格会比使用 Ray Marching 方法更快。

希望这会有所帮助。

编辑:

不幸的是,我从来没有这样的需要,所以我没有任何示例代码,但这里有一些伪代码可以帮助你:

你有你的网格:

Mesh mesh;

顶点邻居数组:

对于任何顶点索引NtriNeighbors[N]将存储由边连接的其他顶点的索引

List<HashSet<int>>     triNeighbors = new List<HashSet<int>>();

int[] meshTriangles = mesh.triangles;
// iterate vert indices per triangle and store neighbors
for( int i = 0; i < meshTriangles.Length; i += 3 ) 
    // three indices making a triangle
    int v0 = meshTriangles[i];
    int v1 = meshTriangles[i+1];
    int v2 = meshTriangles[i+2];
    int maxV = Mathf.Max( Mathf.Max( v0, v1 ), v2 );

    while( triNeighbors.Count <= maxV )
        triNeighbors.Add( new HashSet<int>() );

    triNeighbors[v0].Add( v1 );
    triNeighbors[v0].Add( v2 );

    triNeighbors[v1].Add( v0 );
    triNeighbors[v1].Add( v2 );

    triNeighbors[v2].Add( v0 );
    triNeighbors[v2].Add( v1 );

现在,对于任何单个顶点,您可以使用索引 N 计算其新的平均位置,例如:

int counter = 0;
int N = 0;
Vector3 sum = Vector3.zero;
if( triNeighbors.Count > N && triNeighbors[N] != null )

    foreach( int V in triNeighbors[N] ) 
        sum += mesh.vertices[ V ];
        counter++;
    
    sum /= counter;

此代码中可能存在一些错误,我只是弥补,但您应该明白这一点。

【讨论】:

非常感谢!最后一种方法可能确实是最合适的方法之一。我已经将所有部分合并到一个网格中(使用 Unity 的 CombineMeshes)。你有一些 C# 示例代码可以让我开始使用顶点位置平均器吗? @PhilippLenssen 我给你写了一些示例伪代码。 谢谢!此外,生成的网格显示平滑的表面也很重要……“平滑角度”设置可以在 Unity 的 obj 模型导入器中调整接近法线设置。 为了平滑你需要类似的结构,对于任何给定的顶点索引 N 存储所有使用它的三角形。然后计算这些三角形的法线,将它们相加并归一化——这就是顶点法线。 您也可以尝试这样的技巧 - 您已经拥有任何给定顶点的所有顶点邻居。它们不在一个平面上,但如果你能找到一个尽可能靠近它们的平面,那么它的法线将非常接近你需要的值。只是一个尝试的想法。

以上是关于在 Unity 中平滑动态生成的网格?的主要内容,如果未能解决你的问题,请参考以下文章

unity中动态生成网格

Unity 动态网格地图的生成:基于Perlin Noise创建地形

如何在 ARKit 中平滑多边形网格?

如何在 STL 加载的 BufferGeometry 中平滑网格三角形

如何在unity3D中平滑移动相机?

游戏开发进阶Unity网格探险之旅(Mesh | 动态合批 | 骨骼动画 | 蒙皮 )