Three.js(17)——Shadows(二)

135 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情 >>

今天我们来讨论聚光灯阴影与点光源阴影,以及烘焙阴影。

聚光灯阴影

创建聚光灯

// Spot Light
const spotLight = new THREE.SpotLight(0xffffff, 0.3, 10, Math.PI * 0.3);
spotLight.castShadow = true;
spotLight.position.set(0, 2, 2);
scene.add(spotLight);
// 如果要使聚光灯看向某处记得把target添加场景中
scene.add(spotLight.target);

创建相机助手

const spotLightCameraHelper = new THREE.CameraHelper(
  spotLight.shadow.camera
);
scene.add(spotLightCameraHelper);

优化聚光灯阴影

因为它是聚光灯,使用的是透视相机PerspectiveCamera,所以可以通过fov属性改变摄像机视锥体垂直视野角度

spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.fov = 30;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 6;

下图中,我们可以看到聚光灯的阴影,以及聚光灯相机助手:

微信截图_20220823160607.png

点光源阴影

创建点光源

// Point light
const poingtLight = new THREE.PointLight(0xffffff, 0.3);
poingtLight.castShadow = true;
poingtLight.position.set(-1, 1, 0);
scene.add(poingtLight);

创建相机助手

const poingtLightHelper = new THREE.CameraHelper(
  poingtLight.shadow.camera
);
// poingtLightHelper.visible = false;
scene.add(poingtLightHelper);

优化点光源阴影

点光源摄像机使用的也是透视相机,但最好不要去改变它的视锥体垂直视野角度fov属性

poingtLight.shadow.mapSize.width = 1024;
poingtLight.shadow.mapSize.height = 1024;
poingtLight.shadow.camera.near = 0.1;
poingtLight.shadow.camera.far = 5;

下图中,我们可以看到点光源的阴影,以及点光源相机助手:

微信截图_20220823161127.png

烘焙阴影

烘培阴影是Three.js阴影的一个很好的替代品。我们可以将阴影集成到纹理中,并将其应用到材质上。 我们先关闭渲染器的阴影贴图渲染,这样就看不到场景中的阴影了。

renderer.shadowMap.enabled = false;

设置平面 plane 的材质纹理为烘焙阴影贴图:

const textureLoader = new THREE.TextureLoader();
const backedShadow = textureLoader.load("./img/bakedShadow.jpg");
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(5, 5),
  new THREE.MeshBasicMaterial({ map: backedShadow })
);

这种情况只适合于静态物体,当物体运动起来,阴影并不会跟随物体移动。

微信截图_20220823162008.png

备选方案

我们可以使用更简单的烘焙阴影贴图并移动它,使其一直保持在球体下方。

const simpleShadow = textureLoader.load("./img/simpleShadow.jpg");

创建一个略高于地板的平面,将它的材质的 alphaMap 属性设置为简单阴影纹理贴图:

const sphereShadow = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1.5, 1.5),
  new THREE.MeshBasicMaterial({
    color: 0x000000,
    transparent: true,
    alphaMap: simpleShadow,
  })
);
sphereShadow.rotation.x = -Math.PI * 0.5;
sphereShadow.position.y = plane.position.y + 0.01;
scene.add(sphereShadow);

给球体 sphere 添加动画:

// Update the sphere
// 圆周运动
sphere.position.x = Math.cos(elapsedTime) * 1.5;
sphere.position.z = Math.sin(elapsedTime) * 1.5;
// 触底弹跳
sphere.position.y = Math.abs(Math.sin(elapsedTime * 3));

更新平面 sphereShadow 的位置:

// Update the shadow
sphereShadow.position.x = sphere.position.x;
sphereShadow.position.z = sphere.position.z;
sphereShadow.material.opacity = (1 - sphere.position.y) * 0.5;

最终效果:

微信截图_20220823162754.png