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

Make position morph binding as optional in renderers + enable other attrib type morph #16024

Merged
merged 11 commits into from
Dec 31, 2024

Conversation

noname0310
Copy link
Contributor

In #16014 it is now possible to disable position morphs, but since we changed all renderers that use morph targets to unconditionally add the MORPHTARGETS_POSITION define, I tried to use those renderers with meshes that don't have position morphs and found that it caused problems.

This PR fixes the issue mentioned above and enables normal, tangent, uv, and uv2 morphing, which were present in the renderers' shader code but not working in practice. (This is an additional issue I discovered while editing the code)

This is a list of fixed renderers Each of the morph types supported by these renderers existed in the shader but were not actually working because the isReady function did not add the appropriate attributes and define:

  • DepthRenderer - uv, uv2
  • EffectLayer - uv, uv2
  • GeometryBufferRenderer - normal, uv, uv2
  • OutlineRenderer - normal, uv, uv2
  • ShadowGenerator - normal, uv, uv2
  • VolumetricLightScatteringPostProcess - uv, uv2

This is a list of fixed renderers that had code that did not account for the case where the Morph Target Manager does not support Position Morph:

  • ComputeShaderBoundingHelper
  • TransformFeedbackBoundingHelper
  • MorphTargetsBlock

The following defines have been added to handle cases where the list of morph data types that the MorphTargetManager has on the texture does not match the list of morph data types required by the shader.

MORPHTARGETS_SUPPORTPOSITIONS
MORPHTARGETS_SUPPORTNORMALS
MORPHTARGETS_SUPPORTTANGENTS
MORPHTARGETS_SUPPORTUVS
MORPHTARGETS_SUPPORTUV2S

See morphTargetsVertex.fx for more details

@bjsplat
Copy link
Collaborator

bjsplat commented Dec 21, 2024

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Dec 21, 2024

@bjsplat
Copy link
Collaborator

bjsplat commented Dec 21, 2024

@bjsplat
Copy link
Collaborator

bjsplat commented Dec 21, 2024

@Popov72
Copy link
Contributor

Popov72 commented Dec 21, 2024

Thanks, I'd forgotten that we can have data in the morph texture that we don't use in the shader code if the user decides to disable them!

However, I don't think your code is right yet.

MorphTargetManager.supportPositions will only return true if the manager has data for the positions AND if the user enabled the use of this data. So, something like this is wrong:

if (manager.supportsPositions) {
    defines.push("#define MORPHTARGETS_SUPPORTPOSITIONS");
    defines.push("#define MORPHTARGETS_POSITION");
}

We should push MORPHTARGETS_SUPPORTPOSITIONS as soon as the manager has data for positions, not just if the user has chosen to use it. manager._supportsPositions is the property that should be tested instead :

if (manager._supportsPositions) {
    defines.push("#define MORPHTARGETS_SUPPORTPOSITIONS");
}
if (manager.supportsPositions) {
    defines.push("#define MORPHTARGETS_POSITION");
}

However, this property is private, and I think we should make the code clearer. I suggest renaming all the _supportsXXX in MorphTargetManager to _hasXXX (moreover, this will match the name of these properties in MorphTarget) and adding some hasPositions, hasNormals, etc. getters. The above code would become :

if (manager.hasPositions) {
    defines.push("#define MORPHTARGETTEXTURE_HASPOSITIONS");
}
if (manager.supportsPositions) {
    defines.push("#define MORPHTARGETS_POSITION");
}

Note that I've also changed the name of the first define, for greater clarity.

Also, #if defined(MORPHTARGETS_SUPPORTPOSITIONS) || defined(MORPHTARGETS_POSITION) can be simplified to #if defined(MORPHTARGETS_SUPPORTPOSITIONS), as MORPHTARGETS_SUPPORTPOSITIONS will always be defined if MORPHTARGETS_POSITION is defined (with the rename, it would be #if defined(MORPHTARGETTEXTURE_HASPOSITIONS)).

How does it look like?

Note: of course, these changes should also be done for the other targets (normals, tangents, etc)!

[EDIT] I forgot to comment on one thing. Some of the renderers didn't push certain morph targets because the shader code doesn't use them (uv for the depth renderer, for example). However, I guess there's no problem keeping your changes, as the corresponding shader code will be dead code and will be removed by the compiler.

@noname0310
Copy link
Contributor Author

noname0310 commented Dec 21, 2024

Thank you for your response on the weekend.

Also, #if defined(MORPHTARGETS_SUPPORTPOSITIONS) || defined(MORPHTARGETS_POSITION) can be simplified to #if defined(MORPHTARGETS_SUPPORTPOSITIONS), as MORPHTARGETS_SUPPORTPOSITIONS will always be defined if MORPHTARGETS_POSITION is defined (with the rename, it would be #if defined(MORPHTARGETTEXTURE_HASPOSITIONS))

The reason I set this condition is because of backwards compatibility.
If this PR is merged, external renderer (shader) code that doesn't take MORPHTARGETS_SUPPORTTPOSITIONS into account will not work. However, if that's not a big deal, we can change this to a simple test as you suggest.
(Honestly, given that the current implementation of morph targets doesn't support any morphing other than position morph across several renderers, and there are no reports of it, I'm guessing that no one has used morph targets that deeply yet.)

[EDIT] I forgot to comment on one thing. Some of the renderers didn't push certain morph targets because the shader code doesn't use them (uv for the depth renderer, for example). However, I guess there's no problem keeping your changes, as the corresponding shader code will be dead code and will be removed by the compiler.

The depth renderer uses UV morphs. If you look at the shader code, you can see that the alpha test uses a uv with a UV morph applied. When modifying the other renderers, I referenced the shader code to make sure that only the morph types used are added to the defines.

If there's anything else wrong, please let me know. I'm applying what you requested.

@Popov72
Copy link
Contributor

Popov72 commented Dec 21, 2024

Thank you for your response on the weekend.

Also, #if defined(MORPHTARGETS_SUPPORTPOSITIONS) || defined(MORPHTARGETS_POSITION) can be simplified to #if defined(MORPHTARGETS_SUPPORTPOSITIONS), as MORPHTARGETS_SUPPORTPOSITIONS will always be defined if MORPHTARGETS_POSITION is defined (with the rename, it would be #if defined(MORPHTARGETTEXTURE_HASPOSITIONS))

The reason I set this condition is because of backwards compatibility. If this PR is merged, external renderer (shader) code that doesn't take MORPHTARGETS_SUPPORTTPOSITIONS into account will not work. However, if that's not a big deal, we can change this to a simple test as you suggest. (Honestly, given that the current implementation of morph targets doesn't support any morphing other than position morph across several renderers, and there are no reports of it, I'm guessing that no one has used morph targets that deeply yet.)

Yes, I think we can keep the simplified code, and see if anyone complains that we've broken their code.

[EDIT] I forgot to comment on one thing. Some of the renderers didn't push certain morph targets because the shader code doesn't use them (uv for the depth renderer, for example). However, I guess there's no problem keeping your changes, as the corresponding shader code will be dead code and will be removed by the compiler.

The depth renderer uses UV morphs. If you look at the shader code, you can see that the alpha test uses a uv with a UV morph applied. When modifying the other renderers, I referenced the shader code to make sure that only the morph types used are added to the defines.

My bad, I even checked the code of the depth renderer before replying...

Copy link
Member

@sebavan sebavan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we factor the value 0 in PrepareDefinesAndAttributesForMorphTargets ?

It looks like we almost always use it like this:

mesh.morphTargetManager ? PrepareDefinesAndAttributesForMorphTargets(...) : 0 ?

@noname0310
Copy link
Contributor Author

Should we factor the value 0 in PrepareDefinesAndAttributesForMorphTargets ?

It looks like we almost always use it like this:

mesh.morphTargetManager ? PrepareDefinesAndAttributesForMorphTargets(...) : 0 ?

It would be a performance benefit to run PrepareDefinesAndAttributesForMorphTargets function after a null check, because most of scenes with a large number of meshes are dealing with 100s to thousands of meshes without a morph target manager.

@sebavan
Copy link
Member

sebavan commented Dec 30, 2024

Just one conflict to solve and good to go

@noname0310
Copy link
Contributor Author

The conflict has been resolved.

@sebavan sebavan merged commit 3aa6009 into BabylonJS:master Dec 31, 2024
14 checks passed
noname0310 added a commit to noname0310/babylon-mmd that referenced this pull request Jan 1, 2025
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.

4 participants