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

标签 unity3d 3d mesh

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

enter image description here
草图前后

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

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

最佳答案

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

行军方块

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

射线行进和有符号距离函数

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

在这里你可以看到一些例子:
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;
    }
    

    这段代码可能有一些错误,我刚刚补上了,但你应该明白这一点。

    关于unity3d - 在 Unity 中平滑动态生成的网格?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50273477/

    相关文章:

    c# - 使用带偏移的 Raycast 命中点移动对象

    在 3D 层/网格中组装节点的算法

    graphics - Love2D 中网格上的奇怪纹理行为,需要帮助才能达到我期望的结果

    c# - 如何在不丢失任何形状的情况下简化基本网格?

    c# - 按钮 onClick 不会与对象发生碰撞

    ios - 在原生 iOS 应用程序中使用 Unity 动画

    unity3d - 如何在非 GUI 元素中使用 "9-sliced"图像类型?

    java - "Is Triangle Touching"代码未给出正确结果

    c++ - 3D 相机变换对 2D 图像像素的影响

    javascript - 转换阴影