Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smooth dynamically generated mesh in Unity?

Tags:

unity3d

mesh

3d

Given a mesh in Unity & C# (that itself was created in realtime by merging simpler base meshes), how could we during runtime* turn it into a smooth, almost like wrapped-in-cloth mesh version of itself? Not quite a fully convex version, but more rounded, softening sharp edges, bridging deep gaps and so on. The surface would also ideally look like when the "smoothing angle" normals setting is applied to imported objects. Thanks!

enter image description here Before & after sketch

*The mesh setup is made by people and its specifics unknown beforehand. All its basic shape parts (before we merge them) are known though. The base parts may also remain unmerged if that helps a solution, and it would be extra terrific if there was a runtime solution that would fastly apply the wrapper mash even with base parts that change their transform over time, but a static one-time conversion would be great too.

(Some related keywords may be: marching cube algorithm & metaballs, skin above bones, meshfilter converting, smoothing shader, softening, vertices subdivision.)

like image 245
Philipp Lenssen Avatar asked May 10 '18 12:05

Philipp Lenssen


1 Answers

There are many ways to get something similar so you can pick your preferred one:

Marching Cubes

This algorithm is easy to use but the result always inherits the blocky 'style' of it. If that's the look you want then use it. If you need something more smooth and/or pixel perfect then look for other ways.

Ray Marching and Signed Distance Functions

This is quite interesting technique that may give you a lot of control. You can represent your base parts with simple cube/cylinder/etc. equations and blend them together with simple math.

Here you can see some examples: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm

The best thing here is that it's very simple to setup, you don't even need to merge your base parts, you just push your data to renderer. Worse, is that it may get computationaly hard on rendering part.

Old school mesh modifications

Here you have the most options but it's also most complicated. You start with your base parts which don't have much data by themselves so you should probably join them into one mesh using CSG Union operation.

Having this mesh you can compute neighbors data for your primitives:

  • for each vertex find triangles containing it.
  • for each vertex find edges containing it.
  • for each edge find triangles containing it.

etc.

With such data you may be able to do things like:

  • Find and cut some sharp vertex.
  • Find and cut some sharp edge.
  • Move the vertex to minimize angle between triangles/edges it creates.

and so on...

There are really a lot of details that may work for you or not, you just need to test some to see which one gives the preferred results .

One simple thing I'd start with:

  • For each vertex find all vertices connected to it by any edge.
  • Compute average position of all those vertices.
  • Use some alpha parameter in [0,1] range to blend between initial vertex position and averaged one.
  • Implement multiple iterations of this algorithm and add parameter for it.
  • Experiment with alpha and number of iterations.

Using this way you also have two distinct phases: computation and rendering, so doing it with animation may become too slow, but just rendering the mesh will be faster than in Ray Marching approach.

Hope this helps.

EDIT:

Unfortunately I've never had such need so I don't have any sample code but here you have some pseudo-code that may help you:

You have your mesh:

Mesh mesh;

Array of vertex neighbors:

For any vertex index N, triNeighbors[N] will store indices of other vertices connected by edge

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 );
}

Now, for any single vertex, with index N you can compute its new, averaged position like:

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;
}

There may be some bugs in this code, I've just made it up but you should get the point.

like image 89
kolenda Avatar answered Oct 15 '22 21:10

kolenda