How to crossFade two textures with shaders for morphTarget object animations?

I have a very odd scenario happening here, I want making two textures working along each other, one of them will fade, while the other will come up, making a smooth transition.

I made it working just fine for normal objects, or even objects that have animation with them, but when it comes for objects that have morphTarget animations which is exactly what I need, it just won’t work correctly.

In one attempt the object wouldn’t even show up, only its shadow, in another attempt the object has the crossFade effect exactly as I want but the morphTarget animation doesn’t work, the object has its animation happening but the morphTarget will stay in the first position without morphing because of the customShader.

here I have my code:

const CrossFadeMaterial = shaderMaterial(
    {
      effectFactor: 0.5,
      dispFactor: 1,
      tex: null,
      tex2: null,
      disp: null,
    },
    `varying vec2 vUv;
    varying vec3 Position;
    varying vec3 Normal;
    
    void main() {
      #include <skinbase_vertex>
      #include <begin_vertex>
      #include <beginnormal_vertex>
      #include <defaultnormal_vertex>
      #include <skinning_vertex>
      #include <project_vertex>
      #include <morphtarget_vertex>

      Normal = normalize(normalMatrix * normal);
      Position = vec3(modelViewMatrix * vec4(position, 1.0));
      gl_Position = projectionMatrix * mvPosition;
    }`,
    `varying vec2 vUv;
    uniform sampler2D tex;
    uniform sampler2D tex2;
    uniform sampler2D disp;
    uniform float _rot;
    uniform float dispFactor;
    uniform float effectFactor;
    void main() {
      vec2 uv = vUv;
      vec4 disp = texture2D(disp, uv);
      vec2 distortedPosition = vec2(uv.x + dispFactor * (disp.r*effectFactor), uv.y);
      vec2 distortedPosition2 = vec2(uv.x - (1.0 - dispFactor) * (disp.r*effectFactor), uv.y);
      vec4 _texture = texture2D(tex, distortedPosition);
      vec4 _texture2 = texture2D(tex2, distortedPosition2);
      vec4 finalTexture = mix(_texture, _texture2, dispFactor);
      gl_FragColor = finalTexture;
      #include <tonemapping_fragment>
      #include <colorspace_fragment>
    }`
  );

  extend({ CrossFadeMaterial });

and I call the crossFadeMaterial this way inside of the skinnedMesh exactly as the shader needs, the first texture, the second texture, and one texture in between for making the transition:

    <group ref={group} {...props} dispose={null}>
      <group name='Scene'>
        <group name='cheetah-rig-main'>
          <skinnedMesh
            castShadow
            name='cheetah'
            geometry={nodes.cheetah.geometry}
            material={nodes.cheetah.material}
            skeleton={nodes.cheetah.skeleton}
            morphTargetDictionary={nodes.cheetah.morphTargetDictionary}
            morphTargetInfluences={nodes.cheetah.morphTargetInfluences}
          >
            <crossFadeMaterial tex={texture1} tex2={texture2} disp={crossFade}/>
          </skinnedMesh>
          <primitive object={nodes.root} />
          <primitive object={nodes['MCH-torsoparent']} />
          <primitive object={nodes['MCH-foot_ikparentL']} />
          <primitive object={nodes['MCH-thigh_ik_targetparentL']} />
          <primitive object={nodes['MCH-foot_ikparentR']} />
          <primitive object={nodes['MCH-thigh_ik_targetparentR']} />
          <primitive object={nodes['MCH-hand_ikparentL']} />
          <primitive object={nodes['MCH-upper_arm_ik_targetparentL']} />
          <primitive object={nodes['MCH-hand_ikparentR']} />
          <primitive object={nodes['MCH-upper_arm_ik_targetparentR']} />
        </group>
        <primitive object={nodes.neutral_bone} />
      </group>
    </group>

Also as you can see I’ve added the necessary shader chunks for making everything working, but I don’t know if there are any other missing. I will give an example, if I dont add the chunk “#include <morphtarget_vertex>” the object will be transparent even all the other code is present, and when I add it, the crossFade works perfectly but the morphTarget animation doesn’t work, so I suspect that there’s still code missing that will make the morphTarget animation working.

If anyone could help me please, I’d appreciate a lot!

Not entirely sure, but it seems your material might be interfering with or overriding the morphTarget shader chunk. You could try using the Material.onBeforeCompile method to customize the shader.

Check out this article:

You could also use this plugin mentioned in this Discourse thread:

And if you feel brave, you might also explore the Three.js Shading Language (tsl):

1 Like

I’m impressed, these are great resources. Will check them out and see if I can fix it and also take the opportunity to learn more about shaders, thank you for the answer.

2 Likes