有许多方法可以获得类似的东西,因此您可以选择您喜欢的方法:
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;
顶点邻居数组:
对于任意顶点索引N
,triNeighbors[N]
将存储由边连接的其他顶点的索引。
List<HashSet<int>> triNeighbors = new List<HashSet<int>>();
int[] meshTriangles = mesh.triangles;
for( int i = 0; i < meshTriangles.Length; i += 3 ) {
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;
}
这段代码可能存在一些错误,我只是随意编写的,但您应该能够理解要点。