Open hybridherbst opened 2 years ago
Unfortunately a long-standing issue: https://github.com/mrdoob/three.js/issues/14499
Current workarounds are to disable frustum culling for SkinnedMesh objects or to upscale the bounding box of the geometry by some factor.
Oh no :(
I don't have control over threejs in most cases, e.g. when using model-viewer or other existing viewers.
For reference, @mrdoob had mentioned here
that he came up with an efficient solution; but I'm not sure that's already implemented anywhere?
Marking this as duplicate of #14499.
@hybridherbst Maybe you could make a PR in the modelviewer repo disabling frustum culling on SkinnedMesh
?
I actually looked into that but found that it's supposed to already be off!
@elalish do you maybe have an idea what's going on? I can open an issue on model-viewer if you want of course. There's even a unit test that ensures frustum culling is off.
Turns out turning off frustumCulled in the three.js/editor also doesn't affect culling. Maybe there's another issue here? I can confirm that in model-viewer frustumCulled is off for these meshes (actually for all) but the mesh is still culled (or, not rendered).
@Mugen87 can I rename the issue to "SkinnedMeshes ignore the frustumCulled property" and could you reopen it?
Done!
Can you please set the frustumCulled
property to false
for the skinned mesh itself and for all its descendants (via traverse()
)?
If an object gets frustum culled, it does only affect the object itself but not its children.
Can there be invisible descendants for a skinned mesh? In the above screenshot from the three.js editor that's the leaf node, there's no further children as far as I can see.
scene.traverse((node: Object3D) => {
...
// Three.js seems to cull some animated models incorrectly. Since we
// expect to view our whole scene anyway, we turn off the frustum
// culling optimization here.
node.frustumCulled = false;
...
});
Updated the original post with a truncated animation that only shows the part with the biggest issues.
The issue seems to stem from the fact that the "LOD0" object (the parent of the SkinnedMeshRenderer) is scaled to 0 in some frames, and then the SkinnedMesh isn't visible - that might even be expected / by design - usually parent scale shouldn't affect how bones transform a mesh, but with scale=0 it's kind of undefined. Babylon/Unity seem to still show the mesh in that case, but three doesn't. Should it?
I'll look into it some more to verify that this is the issue.
Just to clarify, are you describing one of these two scenarios? Or something else?
(A)
(B)
Scenario A it is - usually the object scale doesn't affect the final result for skinned meshes, since the bones have the ultimate say, but for scale=0 I can see how that might be undefined.
For Scenario B, it would certainly be expected that the result isn't visible (but the skeleton helper / bones would also have scale 0 then).
Here's a minimal repro model: BentCylinder_Scales.zip
From left to right:
three.js/model-viewer:
Babylon:
Unity:
Gestaltor: (yet another variant! The tiny one disappears same as the 0-scale one)
And here's what happens if the values become more extreme, lighting breaks I guess: BentCylinder_Scales_Extreme.zip
The black ones have the SkinnedMesh scaled to 1e11 and 1e-20, respectively.
This has the look of a numerical problem; maybe generating NaNs somewhere?
Yep - I think the questions here would be "is this behaviour defined in glTF" and/or "should three have the same result as Babylon and Unity". Answer might be "this is too much of an edge case" :)
glTF Validator should warn about these cases with NODE_SKINNED_MESH_NON_ROOT
. While the glTF spec says the transform of a skinned mesh must be ignored, skinning is implemented very differently across engines (perhaps Unity especially...) and it's hard to guarantee that a non-invertible matrix — or reaching precision limits — is not going to have side effects.
If I remember correctly, Unity keeps a skinned mesh out of the scene hierarchy entirely, and the transform is completely ignored. three.js does at least compute transforms all the way down, a SkinnedMesh does not get special treatment there, and under some bind modes the SkinnedMesh world matrix can be optionally applied.
GLTFLoader does not change the default, bindMode = "attached"
. That's unintuitive to me, "detached" would sound like what the glTF specification calls for, but it has behaved correctly and ignored parent transforms (other than this scale=0 case) so far.
If there's a simple change in GLTFLoader (e.g. bindMode -> "detached") or WebGLRenderer (e.g. upload identity world matrix for a "detached" SkinnedMesh?) I think we could make that change, I don't think we could make more complex changes for this case.
/cc #17926 I thought there was code somewhere that simply prevented objects with zero scale from rendering, as a workaround for certain cases like flip-board visibility animation. But I can't find that code now, perhaps I was incorrect.
Interesting, the spec even explicitly calls out that implementations MUST not hide a SkinnedMesh if all its axes are scaled to zero: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#:~:text=When%20the%20scale,to%20zero%20simultaneously.
Implementation Note When the scale is zero on all three axes (by node transform or by animated scale), implementations are free to optimize away rendering of the node’s mesh, and all of the node’s children’s meshes. This provides a mechanism to animate visibility. Skinned meshes must not use this optimization unless all of the joints in the skin are scaled to zero simultaneously.
Describe the bug
Seems the calculations for culling away animated skinned meshes aren't fully correct; in this example file, the mesh disappears a lot randomly, especially in the later part of the file.
To Reproduce
Steps to reproduce the behavior:
In model-viewer
In three.js/editor
Live example
Here's a video: https://user-images.githubusercontent.com/2693840/167219369-588fe804-8fab-4c42-a5b4-79d4d38250dd.mp4
If you drop the same file into https://sandbox.babylonjs.com/, the character is visible all the time (as expected).
Expected behavior
Character is always visible
Platform:
EDIT: replaced with a truncated version of the animation, original here: SkinningCullingBug.zip