Friday, January 1, 2021

Morph Transfer Artifacts

Update Jan. 2. 

Xin pointed out that Blender has a built-in method that computes the face which is closest to a given point in space. Using this method to match vertices with faces results in a massive speed-up compared to doing the same thing using Numpy. To transfer one shapekey from the body to the top now takes 1.1 seconds, of which 0.3 seconds is spent on matching vertices and faces, compared to 74 seconds yesterday.

Since the Nearest-Face method is more or less equivalent to the old General method, only much faster, the General method has been renamed to Legacy. I will keep it if it turns out that the new method does not work in some cases.

Original Post

Some time ago we reported on Morph Transfer Progress. However, it turns out that the default Nearest-Vertex method has some problems. First, engetudouiti reported that the transfer tool may run out of memory. This happens because in order to find the nearest vertex, a numpy array was allocated with the distances between all vertex pairs. The size of this array is proportional to the product of the number of vertices in the source and target meshes, and if the meshes are sufficiently dense, the available memory may not suffice to allocate that array.

That problem was straight-forward to fix. Instead of allocating one huge numpy array, a python loop computes the nearest source vertex for each target vertex. The time to do this is still quadratic, but the memory needed is only linear, so there should not be any memory errors.

Then I found another problem when transferring some JCMs to the basic wear top. Here the pJCMChestFwd_35 morph has been transferred to the top, using the Nearest Vertex and General methods. There is a strange net-shaped pattern in the former case.


The pattern arises because the target mesh (the top) is denser than the source mesh (the body), and the size of the pattern is roughly the same as the resolution of the body mesh. The artifact is really inherit in the nearest-vertex method, which we can see as follows.

The general method creates data transfer modifiers to transfer the shapekeys to the target mesh. Since that modifier cannot transfer shapekeys directly, the shapekey information is converted into three vertex groups (for the x, y, and z components), which are then transferred, and finally the vertex groups are converted back into shapekeys of the target mesh. The modifier is set to use the Nearest Face Interpolated mapping. If we use Nearest Vertex as the modifier mapping instead, the same kind of net-shaped pattern arises.
In the latest commits, the Nearest Vertex transfer method is replaced by a Nearest Face method, which basically does the same thing as the General method but using Numpy instead: instead of copying the shapekey from the nearest vertex, it copies it from the nearest surface point.

When we transfer the pJCMChestFwd_35 using the Nearest Face method, the result is very similar to the General method.

Unfortunately, the new method is more time-consuming for large meshes; to transfer one shapekey from the body to the top took 8 seconds with the old Nearest Vertex method, but 74 seconds with the Nearest Face method. Almost all the time is spent finding the closest triangle in the source mesh for each vertex in the target mesh. I hope to be able to find some way to reduce this time in the future.