学习灯光与阴影,详解聚光灯、点光源的属性与应用

简单了解不同灯光和材质

  • Light灯光,能否投射阴影

    • 环境光AmbientLight:环境光会均匀的照亮场景中的所有物体,不能用来投射阴影,因为它没有方向。
    • 平行光DirectionalLight:平行光是沿着特定方向发射的光,可以投射阴影。
    • 点光源PointLight:从一个点向各个方向发射的光源,可以投射阴影。
    • 聚光灯SpotLight:光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。该光源可以投射阴影。
    • 平面光光源RectAreaLight:从一个矩形平面上均匀地发射光线,可以用来模拟像明亮的窗户或者条状灯光光源,但该光源不支持阴影
  • Material材质,能否产生阴影

    • 基础网格材质MeshBasicMaterial:一个以简单着色(平面或线框)方式来绘制几何体的材质,这种材质不受光照的影响
    • Lambert网格材质MeshLambertMaterial:一种非光泽表面的材质(可以模拟木材、石材等),没有镜面高光,可以产生阴影。
    • Phong网格材质MeshPhongMaterial:该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材),可以产生阴影。
    • 标准网格材质MeshStandardMaterial:一种基于物理渲染(PBR)的标准材质,可以产生阴影。在实践中,该材质提供了比MeshLambertMaterialMeshPhongMaterial 更精确和逼真的结果,代价是计算成本更高。
    • 物理网格材质MeshPhysicalMaterial:提供了更高级的基于物理的渲染属性,可以产生阴影,渲染效果更逼真,但也消耗更高的性能。
    • 卡通网格材质MeshToonMaterial:也同样可以基于光照。

初始化Vue代码,只保留场景摄像机物体(球体和平面)光源渲染器轨道控制器坐标轴辅助器

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<template>
<div></div>
</template>

<script>
// 01 灯光与阴影的关系与设置
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

export default {
data() {
return {
scene: null, // 场景
camera: null, // 摄像机
renderer: null, // 渲染器
controls: null // 轨道控制器
}
},
methods: {
init() {
// 1、创建场景对象
this.scene = new THREE.Scene()

// 2、创建摄像机
this.camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
1000 // 摄像机视锥体远端面
)
this.camera.position.set(0, 0, 10) // 设置摄像机位置坐标
this.scene.add(this.camera) // 将摄像机添加到场景中

// 创建物体
// 1、几何体(球缓冲几何体)
const sphereGeometry = new THREE.SphereBufferGeometry(1, 20, 20)
// 2、材质
const material = new THREE.MeshStandardMaterial()
// 3、根据集合体和材质创建物体
const sphere = new THREE.Mesh(sphereGeometry, material)
this.scene.add(sphere)

// 添加平面
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
this.scene.add(plane)

// 灯光
// 环境光
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) // 平行光位置
this.scene.add(directionalLight)

// 4、初始化渲染器
this.renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight)
// console.log(this.renderer) // 渲染器渲染出的canvas画布
// 将WebGL渲染的内容canvas添加到body
document.body.appendChild(this.renderer.domElement)

// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 设置控制器阻尼,让其更有真实效果
this.controls.enableDamping = true

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5)
this.scene.add(axesHelper) // 添加到场景中
},
// 重绘渲染函数
render() {
// 控制器阻尼的调用函数
this.controls.update()
// 使用渲染器,通过相机将场景渲染进来
this.renderer.render(this.scene, this.camera)
// 请求动画帧,渲染下一帧的时候就会调用render函数,也就是回调自身
requestAnimationFrame(this.render)
}
},
mounted() {
this.init()
this.render()

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
// console.log('画面变化了')

// 更新摄像头(宽高比)
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()

// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比(让其等于设备的像素比)
this.renderer.setPixelRatio(window.deviceRixelRatio)
})
}
}
</script>

<style lang="less" scoped>
</style>

01 灯光与阴影的关系与设置

灯光阴影:

  1. 材质要满足对光照有反应
  2. 设置渲染器开启阴影的计算 renderer.shadowMap.enabled = true
  3. 设置光照投射阴影 directionalLight.castShadow = true
  4. 设置物体(球体)投射阴影 sphere.castShadow = true
  5. 设置物体(平面)接收阴影 ``
  • 开启场景中的阴影贴图,设置渲染器开启阴影的计算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
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<template>
<div></div>
</template>

<script>
// 01 灯光与阴影的关系与设置
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

export default {
data() {
return {
scene: null, // 场景
camera: null, // 摄像机
renderer: null, // 渲染器
controls: null // 轨道控制器
}
},
methods: {
init() {
// 1、创建场景对象
this.scene = new THREE.Scene()

// 2、创建摄像机
this.camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
1000 // 摄像机视锥体远端面
)
this.camera.position.set(0, 0, 10) // 设置摄像机位置坐标
this.scene.add(this.camera) // 将摄像机添加到场景中

// 创建物体
// 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)

// 添加平面
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)

// 灯光
// 环境光
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)

// 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)

// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 设置控制器阻尼,让其更有真实效果
this.controls.enableDamping = true

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5)
this.scene.add(axesHelper) // 添加到场景中
},
// 重绘渲染函数
render() {
// 控制器阻尼的调用函数
this.controls.update()
// 使用渲染器,通过相机将场景渲染进来
this.renderer.render(this.scene, this.camera)
// 请求动画帧,渲染下一帧的时候就会调用render函数,也就是回调自身
requestAnimationFrame(this.render)
}
},
mounted() {
this.init()
this.render()

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
// console.log('画面变化了')

// 更新摄像头(宽高比)
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()

// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比(让其等于设备的像素比)
this.renderer.setPixelRatio(window.deviceRixelRatio)
})
}
}
</script>

<style lang="less" scoped>
</style>


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图像界面动态改变平行光投射相机的属性,每次修改其属性都需要更新摄像机投影矩阵,让其重新计算updateProjectionMatrix

    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
    // 导入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
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<template>
<div></div>
</template>

<script>
// 02 平行光阴影属性与阴影相机原理
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 导入dat.gui
import * as dat from 'dat.gui'

export default {
data() {
return {
scene: null, // 场景
camera: null, // 摄像机
renderer: null, // 渲染器
controls: null // 轨道控制器
}
},
methods: {
init() {
// 1、创建场景对象
this.scene = new THREE.Scene()

// 2、创建摄像机
this.camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
1000 // 摄像机视锥体远端面
)
this.camera.position.set(0, 0, 10) // 设置摄像机位置坐标
this.scene.add(this.camera) // 将摄像机添加到场景中

// 创建物体
// 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)

// 添加平面
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)

// 灯光
// 环境光
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 // 设置光照投射阴影

// 设置阴影贴图模糊度
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(() => {
// 更新摄像机投影矩阵,让其重新计算
directionalLight.shadow.camera.updateProjectionMatrix()
})

// 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)

// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 设置控制器阻尼,让其更有真实效果
this.controls.enableDamping = true

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5)
this.scene.add(axesHelper) // 添加到场景中
},
// 重绘渲染函数
render() {
// 控制器阻尼的调用函数
this.controls.update()
// 使用渲染器,通过相机将场景渲染进来
this.renderer.render(this.scene, this.camera)
// 请求动画帧,渲染下一帧的时候就会调用render函数,也就是回调自身
requestAnimationFrame(this.render)
}
},
mounted() {
this.init()
this.render()

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
// console.log('画面变化了')

// 更新摄像头(宽高比)
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()

// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比(让其等于设备的像素比)
this.renderer.setPixelRatio(window.deviceRixelRatio)
})
}
}
</script>

<style lang="less" scoped>
</style>


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
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<template>
<div></div>
</template>

<script>
// 03 详解聚光灯各种属性与应用
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 导入dat.gui
import * as dat from 'dat.gui'

export default {
data() {
return {
scene: null, // 场景
camera: null, // 摄像机
renderer: null, // 渲染器
controls: null // 轨道控制器
}
},
methods: {
init() {
// 1、创建场景对象
this.scene = new THREE.Scene()

// 2、创建摄像机
this.camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
1000 // 摄像机视锥体远端面
)
this.camera.position.set(0, 0, 10) // 设置摄像机位置坐标
this.scene.add(this.camera) // 将摄像机添加到场景中

// 创建物体
// 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)

// 添加平面
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('沿距离的衰减量')

// 4、初始化渲染器
this.renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 开启场景中的阴影贴图
this.renderer.shadowMap.enabled = true
// 使用物理上正确的光照模式
this.renderer.physicallyCorrectLights = true

// console.log(this.renderer) // 渲染器渲染出的canvas画布
// 将WebGL渲染的内容canvas添加到body
document.body.appendChild(this.renderer.domElement)

// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 设置控制器阻尼,让其更有真实效果
this.controls.enableDamping = true

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5)
this.scene.add(axesHelper) // 添加到场景中
},
// 重绘渲染函数
render() {
// 控制器阻尼的调用函数
this.controls.update()
// 使用渲染器,通过相机将场景渲染进来
this.renderer.render(this.scene, this.camera)
// 请求动画帧,渲染下一帧的时候就会调用render函数,也就是回调自身
requestAnimationFrame(this.render)
}
},
mounted() {
this.init()
this.render()

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
// console.log('画面变化了')

// 更新摄像头(宽高比)
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()

// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比(让其等于设备的像素比)
this.renderer.setPixelRatio(window.deviceRixelRatio)
})
}
}
</script>

<style lang="less" scoped>
</style>


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
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<template>
<div></div>
</template>

<script>
// 04 详解点光源属性与应用
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 导入dat.gui
import * as dat from 'dat.gui'

export default {
data() {
return {
scene: null, // 场景
camera: null, // 摄像机
smallBall: null, // 点光源小球
renderer: null, // 渲染器
controls: null, // 轨道控制器
clock: null // 时钟
}
},
methods: {
init() {
// 1、创建场景对象
this.scene = new THREE.Scene()

// 2、创建摄像机
this.camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
1000 // 摄像机视锥体远端面
)
this.camera.position.set(0, 0, 10) // 设置摄像机位置坐标
this.scene.add(this.camera) // 将摄像机添加到场景中

// 创建物体
// 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)

// 添加平面
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)

// 创建一个小球作为点光源“实体”
this.smallBall = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.1, 20, 20),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
this.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)

// 将点光源添加到小球上,小球作为“父级元素”
this.smallBall.add(pointLight)

this.scene.add(this.smallBall)
// 创建GUI
const gui = new dat.GUI()
gui.add(this.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('沿距离的衰减量')

// 4、初始化渲染器
this.renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 开启场景中的阴影贴图
this.renderer.shadowMap.enabled = true
// 使用物理上正确的光照模式
this.renderer.physicallyCorrectLights = true

// console.log(this.renderer) // 渲染器渲染出的canvas画布
// 将WebGL渲染的内容canvas添加到body
document.body.appendChild(this.renderer.domElement)

// 创建轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
// 设置控制器阻尼,让其更有真实效果
this.controls.enableDamping = true

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5)
this.scene.add(axesHelper) // 添加到场景中

// 设置时钟
this.clock = new THREE.Clock()
},
// 重绘渲染函数
render() {
// 让点光源小球绕大球运动
const time = this.clock.getElapsedTime()
this.smallBall.position.x = Math.sin(time) * 3
this.smallBall.position.z = Math.cos(time) * 3
this.smallBall.position.y = 2 + Math.sin(time * 5)

// 控制器阻尼的调用函数
this.controls.update()
// 使用渲染器,通过相机将场景渲染进来
this.renderer.render(this.scene, this.camera)
// 请求动画帧,渲染下一帧的时候就会调用render函数,也就是回调自身
requestAnimationFrame(this.render)
}
},
mounted() {
this.init()
this.render()

// 监听画面变化,更新渲染画面
window.addEventListener('resize', () => {
// console.log('画面变化了')

// 更新摄像头(宽高比)
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()

// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 设置渲染器的像素比(让其等于设备的像素比)
this.renderer.setPixelRatio(window.deviceRixelRatio)
})
}
}
</script>

<style lang="less" scoped>
</style>

这节课的小球让我的笔记本CPU和GPU满载运行,浏览器和电脑卡的要死,哈哈哈


【参考内容】: