Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InstancedSkinnedMesh #22667

Closed
wants to merge 4 commits into from
Closed

InstancedSkinnedMesh #22667

wants to merge 4 commits into from

Conversation

wizgrav
Copy link
Contributor

@wizgrav wizgrav commented Oct 10, 2021

Similar to InstancedMesh, along with an example, basically repeatedly updating bone matrices and calling setBonesAt(index) to fill a texture with all bone matrices for all instances.

Shader code uses gl_InstanceID and texelFetch so it works on WebGL2 only. It can be made backwards compatible with 1 more attribute(for the lack of gl_InstanceID on WebGL1) and uglier code both on js and glsl side. Should we start standardizing on WebGL2, at least for new features?

@mrdoob the usefulness of this is limited by our capability to fill the bone matrix texture. It would be amazing if we could have an (Instanced)AnimationMixer directly filling the bone texture with matrices with as less copying as possible. Then we can reach thousands of independently animated meshes on screen.

Example live here: https://wizgrav.github.io/three.js/examples/webgl_instancing_skinning.html

@Mugen87
Copy link
Collaborator

Mugen87 commented Oct 13, 2021

Um, do we really want to continue the creation of different mesh classes? I just say this because we have no MorphedMesh class in context of morph targets. Having SkinnedMesh is not necessarily required.

How would the change look like if the existing InstancedMesh class is enhanced? Would this require to merge SkinnedMesh back into Mesh?

@wizgrav
Copy link
Contributor Author

wizgrav commented Oct 13, 2021

Um, do we really want to continue the creation of different mesh classes? I just say this because we have no MorphedMesh class in context of morph targets. Having SkinnedMesh is not necessarily required.

How would the change look like if the existing InstancedMesh class is enhanced? Would this require to merge SkinnedMesh back into Mesh?

@Mugen87 Yes probably Mesh and SkinnedMesh would need to be merged. This PR is super light on code because InstancedSkinnedMesh carries both the isInstancedMesh and isSkinnedMesh flags and, to my suprise tbh, that just worked with very minimal changes in the renderer. This actually is a question for @mrdoob, I just followed the existing patterns, imho it's no problem having 4 different classes for the mesh types and I can't imagine we'll be having more mesh classes than these.

I don't really mind having different classes but what kills me is the performance of filling in the bone texture with matrices. In the demo it updates the bones for ~200 instances per frame and the time it takes to update the bones and compute the matrices is slightly more than what is required to render them. Since we repeatedly update a dummy object, a way to skip storing intermediate computations for bone animations and just have the mixer update the bone texture directly would give us huge wins here

@makc
Copy link
Contributor

makc commented Oct 13, 2021

do we really want to continue the creation of different mesh classes?

kinda relevant input from @mrdoob :

Screen Shot 2021-10-13 at 23 28 31

(that fiddle still is not broken, nice)

@wizgrav
Copy link
Contributor Author

wizgrav commented Oct 14, 2021

@makc unity treats it similarly to three in the sense that they have separate renderers(the equivalent of mesh for us) for static and skinned meshes. Instancing they do differently, you have to do minor additions to your shaders and set a flag on the material. Unity doesn't come with skinned instancing out of the box and because even the static instances need one to one representation in the scenegraph it will always be less efficient than a custom solution.

Personally I prefer the three approach which gives you direct access to the instancing attributes/textures as it allows to get ultra efficient even using wasm to feed the instancing attributes/textures. The matrix math and scenegraph traversal we have are inherently slow because js works with doubles and heavy structures. Going to wasm+floats+simd to work on Float32Arrays directly and expose these arrays to be used for the instancing attributes/textures would speed things up an order of magnitude, better than what unity could deliver if they actually had instanced skinning because they'd still need to go through the scenegraph, which is more efficient for them than us since they work with floats and simd already.

The code paths in the three renderer are pretty clean so as long as an object carries the appropriate flags like isInstancedMesh, isSkinnedMesh, isPoints and the appropriate structures for them (instanceMatrix, instanceColor, count, bindMatrix, skeleton, etc) things will just work, or at least we're not very far away from that code wise. So I guess we could have a method of compositing properties of a generic object. So isPoints + isInstanced along with the structures for both should give us an InstancePoints mesh. And we could have several classes with fixed configurations like InstancedMesh or SkinnedLines etc

@wizgrav
Copy link
Contributor Author

wizgrav commented Oct 14, 2021

@Mugen87 @mrdoob how about a single Drawable class with flags and setters to configure it on the fly? ie Drawable.isSkinned = true would setup the appropriate structures in conjunction with the rest of the configuration. Some flags would be mutually exclusive like isMesh and isPoints etc. IsInstanced would apply to all

@wizgrav
Copy link
Contributor Author

wizgrav commented Oct 15, 2021

Related issue: #22695

@mrdoob @Mugen87 turns out rendering the skinnedmeshes as points was very easy as suspected. With a very minor reshuffling and adding the skinning bits to points.glsl all we need to do is attach a PointMaterial and set (Instanced)SkinnedMesh.isPoints=true.

I updated the example to toggle between mesh and points rendering by clicking. https://wizgrav.github.io/three.js/examples/webgl_instancing_skinning.html

Untitled

@wizgrav
Copy link
Contributor Author

wizgrav commented Oct 18, 2021

Closing this to bring it back more comprehensive

@wizgrav wizgrav closed this Oct 18, 2021
@aiden-jeffrey
Copy link

@wizgrav - do you plan to resurrect this?

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 7, 2022

There is a solution for WebGPU: https://threejs.org/examples/webgpu_skinning_instancing

@Skarian
Copy link

Skarian commented Jun 15, 2022

@Mugen87 Hi, so awesome that its available in WebGPU but considering WebGPU is not in the mainstream as of yet to my knowledge, I think there would still be a lot of benefit to having a WebGL2 equivalent for the time being. @wizgrav 's solution looks pretty solid from what I can tell.

If there are other solutions I have not found yet please let me know :)

@CodyJasonBennett
Copy link
Contributor

CodyJasonBennett commented Feb 2, 2023

There's a way to do this in user-land like https://codesandbox.io/s/2yfgiu although it requires a hack for points rendering -- object.isMesh = !object.isPoints.

@FarazzShaikh
Copy link
Contributor

What would it take to have this in ThreeJS core? I'm willing to put time into a comprehensive PR if someone can point me in the right direction.

@trusktr
Copy link
Contributor

trusktr commented Jul 23, 2023

@FarazzShaikh the API just needs to be clean and make lots of intuitive sense. Can't be a hodge podge like in some other engines. The functionality is already proven out above, but it is the way the classes and code are organized that make a difference (judging from maintainers' good taste for well-organized classes).

So if you can come up with some clean layout for the feature, it may have a higher chance to go in. No one may know exactly what that layout is though.

Maybe the WebGPU sample should be replicated? (Haven't looked)

@Ctrlmonster
Copy link

Out of curiosity, what are the per instance animation setting capabilties in this PR?
I've been writing a high-level animation system sitting on top of three's that includes things like Blend Trees and Animation State Machines and for those to work with instances I would need to be able to:

  • play different (and multiple) animation actions per instance
  • set different times per instance
  • set different action weights per instance
  • set different action timeScales per instance

I might might be missing obvious technical challenges with doing that per instance, but without at least playing different actions per instance, the usability is quite limited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants