为什么 Three.js 投射阴影在 3D 模型上不起作用

Tia*_*iro 2 javascript 3d three.js mapbox-gl-js gltf

我有这样的 3D 模型:

模型

我想添加与此类似的投射阴影:

阴影

我有以下负责模型的代码:

var ambLight = new THREE.AmbientLight( 0x404040 );
this.scene.add(ambLight)

var loader = new THREE.GLTFLoader();
loader.load(path,function (gltf) {
 gltf.scene.traverse( function( model ) {
  if (model.isMesh){ 
   model.castShadow = true; 
  }
 });
 this.scene.add(gltf.scene);
}
Run Code Online (Sandbox Code Playgroud)

我添加了此 StackOverflow帖子castSHadow中所示的部分。

我已经尝试过model.castShadow = true,也尝试过消除该if条件并保留该条件,castShadow但这也不起作用。我是不是少了一步?如果有帮助的话,完整的自定义层代码在这里。

Mug*_*n87 7

  • 您的场景中只有一个AmbientLight没有阴影投射光的实例。
  • 3D 对象仅在设置为 且材质未熄灭时才能接收Object3D.receiveShadow阴影true。意义MeshBasicMaterial不能作为地面的材料。
  • 您必须通过以下方式全局启用阴影:renderer.shadowMap.enabled = true;

我建议你仔细看看这个官方示例的阴影设置。


jsc*_*tro 5

根据您的代码,您尝试在 Mapbox 顶部添加阴影。

为此,除了 @Mugen87 的建议之外,您还需要创建一个表面来接收阴影,并将其放置在您正在加载的模型的正下方,同时还要考虑您正在加载的对象的大小,以避免阴影从平面上消失...然后你就会得到这个。

在此输入图像描述

我在这个小提琴中创建了相关代码。为了清晰起见,我稍微改变了灯光并添加了灯光助手。

var customLayer = {
  id: '3d-model',
  type: 'custom',
  renderingMode: '3d',
  onAdd: function(map, gl) {
    this.camera = new THREE.Camera();
    this.scene = new THREE.Scene();

    const dirLight = new THREE.DirectionalLight(0xffffff, 1);
    dirLight.position.set(0, 70, 100);
    let d = 1000;
    let r = 2;
    let mapSize = 8192;
    dirLight.castShadow = true;
    dirLight.shadow.radius = r;
    dirLight.shadow.mapSize.width = mapSize;
    dirLight.shadow.mapSize.height = mapSize;
    dirLight.shadow.camera.top = dirLight.shadow.camera.right = d;
    dirLight.shadow.camera.bottom = dirLight.shadow.camera.left = -d;
    dirLight.shadow.camera.near = 1;
    dirLight.shadow.camera.far = 400000000;
    //dirLight.shadow.camera.visible = true;

    this.scene.add(dirLight);
    this.scene.add(new THREE.DirectionalLightHelper(dirLight, 10));


    // use the three.js GLTF loader to add the 3D model to the three.js scene
    var loader = new THREE.GLTFLoader();
    loader.load(
      'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
      function(gltf) {
        gltf.scene.traverse(function(model) {
          if (model.isMesh) {
            model.castShadow = true;
          }
        });
        this.scene.add(gltf.scene);
        // we add the shadow plane automatically 
        const s = new THREE.Box3().setFromObject(gltf.scene).getSize(new THREE.Vector3(0, 0, 0));
        const sizes = [s.x, s.y, s.z];
        const planeSize = Math.max(...sizes) * 10;
        const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
        const planeMat = new THREE.ShadowMaterial();
        planeMat.opacity = 0.5;
        let plane = new THREE.Mesh(planeGeo, planeMat);
        plane.rotateX(-Math.PI / 2);
        plane.receiveShadow = true;
        this.scene.add(plane);
      }.bind(this)
    );
    this.map = map;

    // use the Mapbox GL JS map canvas for three.js
    this.renderer = new THREE.WebGLRenderer({
      canvas: map.getCanvas(),
      context: gl,
      antialias: true
    });

    this.renderer.autoClear = false;
    this.renderer.shadowMap.enabled = true;

  },
  render: function(gl, matrix) {
    var rotationX = new THREE.Matrix4().makeRotationAxis(
      new THREE.Vector3(1, 0, 0),
      modelTransform.rotateX
    );
    var rotationY = new THREE.Matrix4().makeRotationAxis(
      new THREE.Vector3(0, 1, 0),
      modelTransform.rotateY
    );
    var rotationZ = new THREE.Matrix4().makeRotationAxis(
      new THREE.Vector3(0, 0, 1),
      modelTransform.rotateZ
    );

    var m = new THREE.Matrix4().fromArray(matrix);
    var l = new THREE.Matrix4()
      .makeTranslation(
        modelTransform.translateX,
        modelTransform.translateY,
        modelTransform.translateZ
      )
      .scale(
        new THREE.Vector3(
          modelTransform.scale,
          -modelTransform.scale,
          modelTransform.scale
        )
      )
      .multiply(rotationX)
      .multiply(rotationY)
      .multiply(rotationZ);

    this.camera.projectionMatrix = m.multiply(l);
    this.renderer.state.reset();
    this.renderer.render(this.scene, this.camera);

    this.map.triggerRepaint();
  }
};
Run Code Online (Sandbox Code Playgroud)