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

9
在Unity&C#中给定一个网格(它本身是通过合并更简单的基础网格实时创建的),我们如何在运行时将其转换为平滑的,几乎像包裹在布料中的网格版本?不完全是完全凸面的版本,而是更圆润,软化锐利的边缘,填补深度间隙等等。表面看起来也像应用于导入对象的“平滑角度”法线设置时的样子。谢谢! enter image description here 之前和之后的草图 *网格设置由人制作,其具体规格事先未知。但是所有基本形状部分(在我们合并它们之前)都是已知的。如果这有助于解决方案,基础部分也可能保持未合并状态,并且如果有一种运行时解决方案可以快速应用包装器网格,即使基础部分随时间改变其变换,那将是非常好的,但是静态的一次性转换也很棒。
(一些相关关键字可能是:Marching Cube算法和Metaballs,骨骼上方的皮肤,MeshFilter转换,平滑着色器,软化,顶点细分。)

1
你需要这个东西吗:(https://keelo.itch.io/raymarching-toolkit-for-unity)? - Seyed Morteza Kamali
看起来很有趣,我会试一试的,谢谢! - Philipp Lenssen
这种方法在很大程度上是有效的。 - Philipp Lenssen
它看起来很完美。但当我将这段代码附加到一个对象上时,它会消失。我应该使用射线行进算法吗? - Seyed Morteza Kamali
1个回答

10
有许多方法可以获得类似的东西,因此您可以选择您喜欢的方法:
Marching Cubes
这个算法很容易使用,但结果总是继承它的方块“风格”。如果这是您想要的外观,则使用它。如果您需要更平滑和/或像素完美的东西,则寻找其他方法。
Ray Marching和Signed Distance Functions
这是一种非常有趣的技术,可以让您拥有很多控制权。您可以用简单的立方体/圆柱等方程表示基本部件,并通过简单的数学将它们混合在一起。
在这里您可以看到一些例子: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm 最好的事情在于它非常容易设置,您甚至不需要合并基本部件,只需将数据推送到渲染器即可。更糟糕的是,渲染部分可能会变得计算量很大。
Old school mesh modifications

这里有最多的选项,但也是最复杂的。你可以从基本部件开始,它们本身没有太多数据,因此您应该使用CSG Union操作将它们合并为一个网格。

有了这个网格,您可以计算出原始数据的邻居:

  • 对于每个顶点,找到包含它的三角形。
  • 对于每个顶点,找到包含它的边缘。
  • 对于每个边缘,找到包含它的三角形。

等等。

有了这样的数据,您可能能够做到以下事情:

  • 查找并削减一些尖锐的顶点。
  • 查找并削减一些尖锐的边缘。
  • 移动顶点以使其创建的三角形/边缘之间的角度最小。

等等...

有很多细节可能适合您或不适合您,您只需要测试一些以查看哪个给出所需的结果。

我会建议您从一个简单的事情开始:

  • 对于每个顶点,找到所有与之相连的顶点。
  • 计算所有这些顶点的平均位置。
  • 使用在[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;
}

这段代码可能存在一些错误,我只是随意编写的,但您应该能够理解要点。


这只是一个快速而简单的伪代码,我从未在我的一侧编译过它。你能给我们展示你遇到的错误吗? - kolenda
我已将其复制到我的项目中并修复了所有错误,请检查更新后的代码。如果您仍有问题,请告诉我。 - kolenda
抱歉,代码中的“foreach (int V in triNeighbors[N]) {”这一行一直报错(未定义N,V无法转换为int)-- 如果您能正常运行,请问您在Unity中使用的是哪个.Net版本?这可能是一个新功能吗?(编辑:嗯,即使我切换到.Net 4,它也无法正常工作。) - Philipp Lenssen
你需要先了解代码的意图。N参数是由你提供的 - 这是你要平均化的顶点的索引。我已经将此参数添加到代码中,请现在尝试。关于 .Net 版本,我目前不确定,等我回到电脑上再查一下。 - kolenda
Unity 2018.1.3f1 和 ".NET 4.x 等效" - kolenda
显示剩余11条评论

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