Search code examples
aframe

Animate Opacity of Aframe Shadow


So, I've been messing around with opacity fade-in/fade-outs of glTF models in Aframe, and have achieved good results using Piotr Adam Milewski's model-opacity script (from here), and have looped my daisy-chained animation sequences using Tired Eyes' animation-manager script (from here).

However, I'm having difficulties trying to work out how to also animate the opacity of the model's shadow, as at the moment its shadow still remains visible after the model is no longer visible.

Demo Link 👇

I've remixed a Glitch (of Ada Rose Cannon's AR Starter Kit) which you can find here to show what I mean (see line 204 in the Glitch for the model fade-in/out).

I'd be really grateful if anyone can shed any light on whether it's possible to animate the Aframe shadow to match the model's opacity. Many thanks, in advance, for any advice 🙂


Solution

  • I'm afraid a built-in solution is not yet ready (issue / PR)

    For a single object, you could just use a ShadowMaterial, which has a opacity property, which could be animated along with the objects opacity:

    <script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
    <script src="https://gftruj.github.io/webzamples/aframe/models/components/model-opacity.js"></script>
    <script>
      AFRAME.registerComponent("relative-shadow", {
        schema: {
          target: { type: "selector" }
        },
        init: function() {
          const mesh = this.el.getObject3D("mesh"); // grab the mesh
          const oldMaterial = mesh.material; // store the old material
          mesh.material = this.material = new THREE.ShadowMaterial(); // apply a shadow material
          oldMaterial.dispose(); // dispose the old material
        },
        update: function() {
          this.opacitySource = this.data.target.components["model-opacity"]; // react to the target being set
        },
        tick: function() {
          if (!this.opacitySource) return; // wait until we can access the opacity value
          // update the opacity using the t-rex material opacity from the component
          this.material.opacity = this.opacitySource.data.opacity;
        }
      })
    </script>
    <a-scene>
      <a-asset-item id="spino" src="https://rawcdn.githack.com/krlosflip22/aframe-ar-sample/c91a7a9dd8b1428bc8e68bc1b5d8641d7241fd1b/spinosaurus.glb"></a-asset-item>
      <a-gltf-model id="trex" position="0 1 -4" shadow="cast: true" scale="0.5 0.5 0.5" src="#spino" model-opacity 
                    animation="property: model-opacity.opacity; to: 0; dur: 1000; dir: alternate; loop: true;"></a-gltf-model>
      
      <a-plane rotation="-90 0 0" scale="40 40 40" 
               relative-shadow="target: #trex" shadow="receive: true"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>


    For multiple objects, a simple yet effective solution would be faking shadows - have transparent radial gradient images below the objects like in the threejs fundamentals example. You could control the opacity of each one of them:

    <iframe width="100%" height="100%" src="https://r105.threejsfundamentals.org/threejs/threejs-shadows-fake.html"></iframe>