みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Three.jsに読み込んだglbファイルの影を他のオブジェクトに投影する 🔦

f:id:kimizuka:20210716190712p:plain

Three.jsでライト(AmbientLight以外)を使うと、凹凸のあるオブジェクトには影が表示されます。
が。他のオブジェクトに対しては影が落ちません。

他のオブジェクトに影を落とすためには、いろいろ設定が必要だったのでまとめておきます。

WebGLRenderer.shadowMapを有効にする

threejs.org

const renderer = new THREE.WebGLRenderer({
  antialias : true
});

renderer.shadowMap.enabled = true;

shadowMapのenabledをtrueにします。

ライトのcastShadowを有効にする

const light = new THREE.DirectionalLight(0xFFFFFF, 1);

light.castShadow = true;

影に影響を与えたいlightのcastShadowをtrueにします。
ただし、AmbientLightは影に影響を与えないので、AmbientLight以外を使いましょう。

影を付けるオブジェクトのcastShadowを有効にする

object.castShadow = true;


影が映るオブジェクトのreceiveShadowを有効にする

const wall = new THREE.Mesh(geometry, material);

wall.receiveShadow = true;

影を付けるオブジェクトのcastShadowと影を映すオブジェクトのreceiveShadowをそれぞれ有効にすればOKです。

f:id:kimizuka:20210716232544p:plain

が。よく見ると影がガビガビです。

影の大きさを設定する

const light = new THREE.DirectionalLight(0xFFFFFF, 1);

light.castShadow = true;
light.shadowMapWidth = 4096;
light.shadowMapHeight = 4096;

デフォルトでは512が設定されているのですが、この値を大きくすると影が綺麗になります。

f:id:kimizuka:20210716233150p:plain

デフォルトが512なので、なんとなく16の倍数を設定してます。


一応、ソースを全部載せておきます。
同一階層にmodel.glbを置けば動くと思います。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>shadow</title>
  <style>
    * {
      margin: 0; padding: 0;
    }

    canvas {
      display: block;
    }
  </style>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r127/three.min.js"></script>
  <script src="https://cdn.rawgit.com/mrdoob/three.js/r127/examples/js/loaders/GLTFLoader.js"></script>
  <script>
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
      60,
      window.innerWidth / window.innerHeight,
      1,
      100
    );

    camera.position.set(0, 1, 3);
    scene.add(camera);

    const renderer = new THREE.WebGLRenderer({
      antialias : true,
      alpha: true
    });

    renderer.shadowMap.enabled = true;
    renderer.outputEncoding = THREE.GammaEncoding;
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);

    document.body.appendChild(renderer.domElement);

    const loader = new THREE.GLTFLoader();
    const url = './model.glb';

    loader.load(
      url,
      (gltf) => {
        const model = gltf.scene;

        model.traverse((object) => {
          if (object.isMesh) {
            object.castShadow = true;
          }
        });

        model.scale.set(1, 1, 1);
        model.position.set(0, 0, 0);

        scene.add(model);

        renderer.setAnimationLoop(tick);
      },
      (err) => {
        console.error(err);
      }
    );

    const light = new THREE.DirectionalLight(0xFFFFFF, 1);

    light.position.set(1, 0, 1);

    light.castShadow = true;
    light.shadowMapWidth = 4096;
    light.shadowMapHeight = 4096;

    scene.add(light);

    const geometry = new THREE.PlaneGeometry(16, 16, 1, 1);
    const material = new THREE.MeshStandardMaterial({
      color: 0xFF0000
    });
    const wall = new THREE.Mesh(geometry, material);

    wall.position.set(0, 0, -4);
    wall.rotation.set(0, 0, 0);
    wall.receiveShadow = true;

    scene.add(wall);

    function tick() {
      renderer.render(scene, camera);
    }
  </script>
</body>
</html>