在 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;
顶点邻居数组:
对于任何顶点索引N
,triNeighbors[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 动态网格地图的生成:基于Perlin Noise创建地形