【Three.js】走进3D的奇妙世界(学习打卡03)
学习灯光与阴影,详解聚光灯、点光源的属性与应用
简单了解不同灯光和材质
Light灯光,能否投射阴影
- 环境光AmbientLight:环境光会均匀的照亮场景中的所有物体,不能用来投射阴影,因为它没有方向。
- 平行光DirectionalLight:平行光是沿着特定方向发射的光,可以投射阴影。
- 点光源PointLight:从一个点向各个方向发射的光源,可以投射阴影。
- 聚光灯SpotLight:光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。该光源可以投射阴影。
- 平面光光源RectAreaLight:从一个矩形平面上均匀地发射光线,可以用来模拟像明亮的窗户或者条状灯光光源,但该光源不支持阴影。
Material材质,能否产生阴影
- 基础网格材质MeshBasicMaterial:一个以简单着色(平面或线框)方式来绘制几何体的材质,这种材质不受光照的影响。
- Lambert网格材质MeshLambertMaterial:一种非光泽表面的材质(可以模拟木材、石材等),没有镜面高光,可以产生阴影。
- Phong网格材质MeshPhongMaterial:该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材),可以产生阴影。
- 标准网格材质MeshStandardMaterial:一种基于物理渲染(PBR)的标准材质,可以产生阴影。在实践中,该材质提供了比MeshLambertMaterial 或MeshPhongMaterial 更精确和逼真的结果,代价是计算成本更高。
- 物理网格材质MeshPhysicalMaterial:提供了更高级的基于物理的渲染属性,可以产生阴影,渲染效果更逼真,但也消耗更高的性能。
- 卡通网格材质MeshToonMaterial:也同样可以基于光照。
初始化Vue代码,只保留场景
、摄像机
、物体(球体和平面)
、光源
、渲染器
、轨道控制器
、坐标轴辅助器
:
1 | <template> |
01 灯光与阴影的关系与设置
灯光阴影:
- 材质要满足对光照有反应
- 设置渲染器开启阴影的计算
renderer.shadowMap.enabled = true
- 设置光照投射阴影
directionalLight.castShadow = true
- 设置物体(球体)投射阴影
sphere.castShadow = true
- 设置物体(平面)接收阴影 ``
开启场景中的阴影贴图,设置渲染器开启阴影的计算WebGLRenderer.shadowMap
1
2
3
4
5
6
7
8
9
10// 4、初始化渲染器
this.renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 开启场景中的阴影贴图
this.renderer.shadowMap.enabled = true
// console.log(this.renderer) // 渲染器渲染出的canvas画布
// 将WebGL渲染的内容canvas添加到body
document.body.appendChild(this.renderer.domElement)设置光照投射阴影DirectionalLight.castShadow
1
2
3
4
5
6
7
8
9// 灯光
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
this.scene.add(light)
// 直线光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(10, 10, 10) // 平行光位置
directionalLight.castShadow = true // 设置光照投射阴影
this.scene.add(directionalLight)设置物体(球体)投射阴影Object3D.castShadow
1
2
3
4
5
6
7
8
9
10// 创建物体
// 1、几何体(球缓冲几何体)
const sphereGeometry = new THREE.SphereBufferGeometry(1, 20, 20)
// 2、材质
const material = new THREE.MeshStandardMaterial()
// 3、根据集合体和材质创建物体
const sphere = new THREE.Mesh(sphereGeometry, material)
// 设置物体(球体)投射阴影
sphere.castShadow = true
this.scene.add(sphere)设置物体(平面)接收阴影Object3D.receiveShadow
1
2
3
4
5
6
7
8// 添加平面
const planeGeometry = new THREE.PlaneBufferGeometry(10, 10) // 面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值
const plane = new THREE.Mesh(planeGeometry, material)
plane.position.set(0, -1, 0)
plane.rotation.x = -Math.PI / 2
// 设置物体(平面)接收阴影
plane.receiveShadow = true
this.scene.add(plane)
完整Vue代码:
1 | <template> |
02 平行光阴影属性与阴影相机原理
平行光阴影对象DirectionalLightShadow
光照阴影模糊度属性LightShadow.radius
1
2
3
4
5
6
7
8
9
10
11
12// 灯光
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
this.scene.add(light)
// 直线光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(5, 5, 5) // 平行光位置
directionalLight.castShadow = true // 设置光照投射阴影
this.scene.add(directionalLight)
// 设置阴影贴图模糊度
directionalLight.shadow.radius = 20设置阴影贴图大小(分辨率),获得更细致的阴影贴图LightShadow.mapSize
1
2
3
4// 设置阴影贴图模糊度
directionalLight.shadow.radius = 20
// 设置阴影贴图的分辨率
directionalLight.shadow.mapSize.set(4096, 4096)设置平行光投射相机的属性LightShadow.camera
1
2
3
4
5
6
7
8
9
10
11// 设置阴影贴图模糊度
directionalLight.shadow.radius = 20
// 设置阴影贴图的分辨率
directionalLight.shadow.mapSize.set(4096, 4096)
// 设置平行光投射相机的属性
directionalLight.shadow.camera.near = 0.5 // 摄像机视锥体近端面
directionalLight.shadow.camera.far = 500 // 摄像机视锥体远端面
directionalLight.shadow.camera.top = 5 // 摄像机视锥体上侧面
directionalLight.shadow.camera.bottom = -5 // 摄像机视锥体下侧面
directionalLight.shadow.camera.left = -5 // 摄像机视锥体左侧面
directionalLight.shadow.camera.right = 5 // 摄像机视锥体右侧面使用
dat.gui
图像界面动态改变平行光投射相机的属性,每次修改其属性都需要更新摄像机投影矩阵,让其重新计算updateProjectionMatrix1
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// 导入dat.gui
import * as dat from 'dat.gui'
// 设置阴影贴图模糊度
directionalLight.shadow.radius = 20
// 设置阴影贴图的分辨率
directionalLight.shadow.mapSize.set(4096, 4096)
// 设置平行光投射相机的属性
directionalLight.shadow.camera.near = 0.5 // 摄像机视锥体近端面
directionalLight.shadow.camera.far = 500 // 摄像机视锥体远端面
directionalLight.shadow.camera.top = 5 // 摄像机视锥体上侧面
directionalLight.shadow.camera.bottom = -5 // 摄像机视锥体下侧面
directionalLight.shadow.camera.left = -5 // 摄像机视锥体左侧面
directionalLight.shadow.camera.right = 5 // 摄像机视锥体右侧面
this.scene.add(directionalLight)
// 创建GUI
const gui = new dat.GUI()
gui
.add(directionalLight.shadow.camera, 'near')
.min(0)
.max(10)
.step(0.1)
.name('相机近端')
.onChange((value) => {
// 更新摄像机投影矩阵,让其重新计算
directionalLight.shadow.camera.updateProjectionMatrix()
})
完整Vue代码:
1 | <template> |
03 详解聚光灯各种属性与应用
聚光灯(SpotLight)
https://threejs.org/docs/index.html?q=sp#api/zh/lights/SpotLight
- 添加聚光灯,设置目标投射位置SpotLight.target为球体对象,设置聚光灯最大投射角度范围SpotLight.angle,设置聚光灯衰减到0的距离SpotLight.distance,设置聚光锥的半影衰减百分比SpotLight.penumbra,设置沿着光照距离的衰减量SpotLight.decay(需给渲染器使用物理上正确的光照模式WebGLRenderer.physicallyCorrectLights)
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
34
35
36
37
38// 添加平面
const planeGeometry = new THREE.PlaneBufferGeometry(50, 50) // 面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值
const plane = new THREE.Mesh(planeGeometry, material)
plane.position.set(0, -1, 0)
plane.rotation.x = -Math.PI / 2
// 设置物体(平面)接收阴影
plane.receiveShadow = true
this.scene.add(plane)
// 灯光
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
this.scene.add(light)
// 聚光灯
const spotLight = new THREE.SpotLight(0xffffff, 1)
spotLight.position.set(5, 5, 5) // 聚光灯位置
spotLight.castShadow = true // 设置光照投射阴影
spotLight.intensity = 2 // 亮度
spotLight.target = sphere // 聚光灯投射目标位置
spotLight.angle = Math.PI / 6 // 聚光灯最大投射角度范围
spotLight.distance = 0 // 聚光灯衰减到0的距离,为0时光永远不消失
spotLight.penumbra = 0 // 聚光锥的半影衰减百分比
spotLight.decay = 0 // 沿着光照距离的衰减量
// 设置阴影贴图模糊度
spotLight.shadow.radius = 20
// 设置阴影贴图的分辨率
spotLight.shadow.mapSize.set(4096, 4096)
this.scene.add(spotLight)
// 创建GUI
const gui = new dat.GUI()
gui.add(sphere.position, 'x').min(-5).max(5).step(0.1).name('球体x轴位置')
gui.add(spotLight, 'angle').min(0).max(Math.PI / 2).step(0.01).name('最大投射角度')
gui.add(spotLight, 'distance').min(0).max(10).step(0.01).name('聚光灯衰减距离')
gui.add(spotLight, 'penumbra').min(0).max(1).step(0.01).name('半影衰减百分比')
gui.add(spotLight, 'decay').min(0).max(2).step(0.01).name('沿距离的衰减量')
完整Vue代码:
1 | <template> |
04 详解点光源属性与应用
点光源(PointLight)
https://threejs.org/docs/index.html?q=poin#api/zh/lights/PointLight
添加点光源,设置聚光灯衰减到0的距离SpotLight.distance,设置沿着光照距离的衰减量SpotLight.decay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 灯光
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
this.scene.add(light)
// 点光源
const pointLight = new THREE.PointLight(0xff0000, 1)
pointLight.position.set(2, 2, 2) // 点光源位置
pointLight.castShadow = true // 设置光照投射阴影
// 设置阴影贴图模糊度
pointLight.shadow.radius = 20
// 设置阴影贴图的分辨率
pointLight.shadow.mapSize.set(4096, 4096)
pointLight.distance = 0 // 点光源衰减到0的距离,为0时光永远不消失
pointLight.decay = 0 // 沿着光照距离的衰减量
this.scene.add(pointLight)
// 创建GUI
const gui = new dat.GUI()
gui.add(pointLight.position, 'x').min(-5).max(5).step(0.1).name('点光源x轴位置')
gui.add(pointLight, 'distance').min(0).max(10).step(0.001).name('点光源衰减距离')
gui.add(pointLight, 'decay').min(0).max(2).step(0.01).name('沿距离的衰减量')创建一个小球作为点光源“实体”,把点光源添加到小球上,让其更加直观
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
34// 灯光
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
this.scene.add(light)
// 创建一个小球作为点光源“实体”
const smallBall = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.1, 20, 20),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
smallBall.position.set(2, 2, 2)
// 点光源
const pointLight = new THREE.PointLight(0xff0000, 1)
// pointLight.position.set(2, 2, 2) // 点光源位置
pointLight.castShadow = true // 设置光照投射阴影
pointLight.distance = 0 // 点光源衰减到0的距离,为0时光永远不消失
pointLight.decay = 0 // 沿着光照距离的衰减量
// 设置阴影贴图模糊度
pointLight.shadow.radius = 20
// 设置阴影贴图的分辨率
pointLight.shadow.mapSize.set(4096, 4096)
// 将点光源添加到小球上,小球作为“父级元素”
smallBall.add(pointLight)
this.scene.add(smallBall)
// 创建GUI
const gui = new dat.GUI()
gui.add(smallBall.position, 'x').min(-5).max(5).step(0.1).name('点光源x轴位置')
gui.add(pointLight, 'distance').min(0).max(10).step(0.001).name('点光源衰减距离')
gui.add(pointLight, 'decay').min(0).max(2).step(0.01).name('沿距离的衰减量')
还可以使用Clock跟踪事件让“光源小球”绕大球运动起来,完整Vue代码:
1 | <template> |
这节课的小球让我的笔记本CPU和GPU满载运行,浏览器和电脑卡的要死,哈哈哈
【参考内容】:
- https://threejs.org/
- 老陈打码_B站入门视频:https://www.bilibili.com/video/BV1Gg411X7FY?p=36
- 麒跃科技_老陈打码:https://www.cpengx.cn/