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

Phi*_*sen 7 3d mesh unity-game-engine

鉴于Unity和C#中的网格(它本身是通过合并更简单的基础网格实时创建的),我们怎么能在运行时将它变成一个平滑的,几乎像布料网格版本一样?不完全凸起的版本,但更圆润,柔化锋利的边缘,桥接深的间隙等.理想情况下,当"平滑角度"法线设置应用于导入的对象时,曲面也会如此.谢谢!

在此输入图像描述 草图之前和之后

*网格设置由人员及其具体事先预知.它的所有基本形状部分(在我们合并它们之前)都是已知的.如果有助于解决方案的话,基础部件也可以保持未合并状态,如果有一个运行时解决方案可以快速应用包装器mash,即使基本部件随时间改变其变换,但是静态一次性转换,这将是非常好的也会很棒.

(一些相关的关键字可能是:行进立方体算法和元球,骨骼上方的皮肤,网格过滤器转换,平滑着色器,软化,顶点细分.)

kol*_*nda 9

有很多方法可以获得类似的东西,所以你可以选择你喜欢的方法:

行进立方体

这个算法很容易使用,但结果总是继承它的块状"风格".如果这是你想要的外观然后使用它.如果你需要更平滑和/或像素完美的东西,那么寻找其他方法.

Ray Marching和有符号距离函数

这是非常有趣的技术,可以给你很多控制.您可以使用简单的立方体/圆柱体/等代表您的基础部件.方程式并将它们与简单的数学混合在一起

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

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

旧学校网格修改

在这里你有最多的选择,但它也是最复杂的.您从基本部件开始,这些部件本身没有太多数据,因此您可能应该使用CSG Union操作将它们连接到一个网格中.

拥有此网格,您可以计算基元的邻居数据:

  • 为每个顶点找到包含它的三角形.
  • 为每个顶点找到包含它的边.
  • 对于每个边找到包含它的三角形.

等等

有了这些数据,您可以执行以下操作:

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

等等...

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

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

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

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

希望这可以帮助.

编辑:

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

你有你的网格:

Mesh mesh;
Run Code Online (Sandbox Code Playgroud)

顶点邻居数组:

对于任何顶点索引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 );
}
Run Code Online (Sandbox Code Playgroud)

现在,对于任何单个顶点,使用索引,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;
}
Run Code Online (Sandbox Code Playgroud)

这段代码中可能存在一些错误,我刚刚完成了但你应该明白这一点.