Is the limit of 4 skinning weights per vertex a hard limit of WebGL?

Models downloaded from Mixamo ( and many other places ) have more than 4 weights per vertex, so loading the model with the FBXLoader returns the message

FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.

This means that the animation sometimes gets a little bit corrupted when loaded in three.js - in particular, I have noticed that the hands can have “splayed fingers” and the head can have unfortunate wobbling.

I have not found any way to load the animated models from Mixamo in another program, edit the animation / model to remove the extra weights, then re-export it for use in three.js. Some corruption always occurs along the way. So the models have to be used “as is”, which works pretty well, except for the above corruption.

So I’m wondering if the limit of 4 skinning weights is a hard limit, or is it something I could adjust in my local branch?

AFAIK, four bones per vertex is a common limitation of certain 3D and game engines (like Unity) but it’s not a hard limit of the underlying 3D API. There are engines like UE 4 which have a higher bone limit (e.g. eight).

Have a look at this thread for more information: https://stackoverflow.com/questions/9043845/glsl-per-vertex-fixed-size-array

Also interesting: https://feedback.unity3d.com/suggestions/8-bones-per-vertex

1 Like

Thanks - I found a couple of places that mentioned there were some possible technical reasons for it. Nowhere very convincing though, however Babylon.js and Unity both have the same limit, hence my thinking that it might be a hard limit.

Here for example, someone says:

To be clear, in WebGL - isn’t there a max influence of 4 bones per vertex due to the size of the VertexBuffer Array?

to which the reply is

you can have more than 1 VertexBuffer though since 2.1, I think. If 4 is exceeded, the additional Weights extra & indices extra will also be filled and used.

I think 2.1 here is referring to WebGL 2.1 so perhaps that is required to lift the limit ( I checked the Babylon.js just in case that’s what it means but no references to skinning there).

The Babylon.js docs also mention this limit, but I have found other places saying it has since been increased so maybe they are out of date.

In any case I’ll do a bit more research and if it’s still not clear I’ll raise it as a suggestion on github.

I don’t think that 2.1 is related to WebGL but to Babylon.js.

Engines normally represent the skin index and weight attribute with a vec4 in the vertex shader. three.js does this here in WebGLProgram.

If more than four bones influence a vertex, a single vec4 is not sufficient. You need additional attributes which “could” lead to a resource bottleneck because the maximum number of vertex attributes is limited (16, 32… it depends on the hardware). This situation is also mentioned in the linked stackoverflow thread of my previous post.

This might be a good idea :+1:

You could basically extend it to any number of weights i guess. But it also has memory and performance reasons and there aren’t really more required for most cases. And since WebGL and JS especially on mobile isn’t the same as native i wouldn’t recommend to increase the weights. Another 4 weights means an additional 32 bytes per vertex.

As @Mugen87 mentioned there is a vertex attributes limit. You could circumvent it by using a texture, with it’s costs, but anyway, 4 weights are reasonable.

A shader applies the weights this way. Most changes if not all would go here, and the individual loaders accordingly.

mat4 boneMatX = getBoneMatrix( skinIndex.x );
    			mat4 boneMatY = getBoneMatrix( skinIndex.y );
    			mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    			mat4 boneMatW = getBoneMatrix( skinIndex.w );
    			
    			vec4 skinVertex = bindMatrix * vec4( pos, 1.0 );
    			vec4 skinned = vec4( 0.0 );
    			skinned += boneMatX * skinVertex * skinWeight.x;
    			skinned += boneMatY * skinVertex * skinWeight.y;
    			skinned += boneMatZ * skinVertex * skinWeight.z;
    			skinned += boneMatW * skinVertex * skinWeight.w;
    			skinned  = bindMatrixInverse * skinned;

Maybe it will help you.

I can’t find any reference to it in Babylon.js, and there is no WebGL 2.1 (yet!) so perhaps they meant OpenGL 2.1? In which case it’s not relevant anyway since WebGL is based on OpenGL 2.0.
Oh well, I’ll call that one a red herring and move on :laughing:

I’ve uploaded my model to sketchfab here and the hand animation is playing correctly, so the limit must not be fixed (assuming the issue is actually related to vertex weights that is).

For reference, here is a screenshot from sketchfab (as the animation should look)

sketchfab

And here is the animation in three.js

three

Note the position of the fingers.

Yeah, I already checked the release notes for Babylon 2.1 and couldn’t find any reference to a change affecting skinning there.

You would have to alter the underlying 3d engine, right?