概述:

阅读完本文,你将学会如何使用 Threejs 平行光源创建阴影以及如何避免阴影渲染过程中的一些坑;

基本原理:

Threejs 阴影的渲染有三个关键的条件:

  1. Render 对象开启阴影渲染能力;
  2. 光源或阴影原物体开启阴影投射;
  3. 显示阴影的物体开启阴影接收;

渲染过程关键代码:

根据上面的原理,以下代码片段演示在实际 threejs 开发中如何进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//1.创建renderer
const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer({
antialias: true,
})
renderer.shadowMap.enabled = true //开启阴影
renderer.shadowMap.type = THREE.VSMShadowMap //设置阴影类型

//2.创建平行光源和光源辅助工具
const light = new THREE.DirectionalLight(0xffffff, item.intensity)
const helper = new THREE.DirectionalLightHelper(light, 5)
light.position.set(10, 10, 10)
// 光源阴影渲染相关设置
light.castShadow = true // 启用阴影投射
light.shadow.bias = -0.00005
light.shadow.camera.left = -5 * 10 // 阴影投射范围
light.shadow.camera.right = 5 * 10 // 阴影投射范围
light.shadow.camera.top = 5 * 10 // 阴影投射范围
light.shadow.camera.bottom = -5 * 10 // 阴影投射范围
light.shadow.mapSize.width = 2048 // 阴影质量宽度
light.shadow.mapSize.height = 2048 // 阴影质量高度

//3.创建一个方块位于坐标2,2,2处
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2)
const cubeMaterial = new THREE.MeshLambertMaterial()
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
cube.castShadow = true //开启阴影投射
cube.position.set(2, 2, 2)

//创建一个平面位于方块下方,开启阴影接收
const planeGeometry = new THREE.PlaneGeometry(20, 20, 32, 32)
const plane = new THREE.Mesh(planeGeometry, new THREE.MeshLambertMaterial())
ground.rotation.set(-Math.PI / 2, 0, 0) //旋转90度使平面水平
ground.receiveShadow = true //开启阴影接收

要点及坑点总结:

1.渲染模型的阴影需要同时开启阴影投射和阴影接收,这是因为复杂的模型在光源下可能会接收到自身产生的阴影

1
2
mesh.castShadow = true
mesh.receiveShadow = true

image-20241120112017386

2.需要确保平行光的阴影相机的投射范围覆盖需要产生阴影的范围

如果没有覆盖,就会出现这种只产生一小块阴影的情况:

image-20241120142117800

正确覆盖:

image-20241120142401454

3.材质的金属度为 1 会导致模型无法漫反射,呈现为纯黑色,这一特性会导致渲染某些材质异常的模型时,模型为纯黑色无法被环境光照亮

这种情况下需要降低模型的 metalness,比如设置为 0.5

1
2
3
4
5
6
7
8
9
10
/* 添加镜面球体 */
const reflectBallGeometry = new THREE.SphereGeometry(1, 32, 32)
const reflectBallMaterial = new THREE.MeshStandardMaterial({
roughness: 0.0, //绝对光滑
metalness: 1, //纯金属
})
const reflectBall = new THREE.Mesh(reflectBallGeometry, reflectBallMaterial)
reflectBall.position.set(3, 3, 3)
reflectBall.castShadow = true
this.scene.add(reflectBall)

镜面球体没有环境贴图时为纯黑:

image-20241120112614610

4.未产生阴影的地方出现条纹,需要设置 bias 参数,具体原理我也不清楚,感兴趣可自行查阅研究:

未设置 bias:

image-20241120142653106

设置了 bias 为很小的数:

light.shadow.bias = -0.00001

image-20241120142741189

5.设置 mapSize 的宽高可以提高阴影质量

1
2
light.shadow.mapSize.width = 2048
light.shadow.mapSize.height = 2048