three.js基础学习之光源

这是three.js基础学习的第15篇博客,前14篇可以点击查看。在本篇中,将学习three.js中关于光光源知识。有AmbientLight环境光,DirectionalLight平行光,HemisphereLight半球光,PointLight点光源,RectAreaLight平面光源,SpotLight聚光灯等。

在开始学习光源基础知识前,先新建一个叫learnThree15.html的文件,然后复制learnThree14.html的代码并删除掉除了基础外的其它部分。如果你懒得做这件事,也可以去(/three/basiccode.html)[three.js基础学习的基础代码]复制。我在这里准备了一份完整的基础代码。

现在,我们正式进入threejs光世界。光给了我们五彩斑斓的世界,控制黑夜与黎明的自然光,远方星空的点点星光,电影院直射幕布的直射光······。这些,three.js都尽可能的进行了模仿。

按照惯例,我们现在应该进入正文了。但是,今天不会。因为光的特殊性,我们需要作一些额外的工作。

  • 添加一个盒子,如果忘记怎们创建盒子可以回到three.js学习之盒子复习,具体代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    const width = 4
    const height = 4
    const depth = 4
    const boxGeometry = new THREE.BoxGeometry(width, height, depth)

    const material = new THREE.MeshStandardMaterial({
    color: 0xe74478
    })

    const box = new THREE.Mesh(boxGeometry, material)
    box.position.set(0, 0, -5)
    box.castShadow = true
    group.add(box)
    }

    代码中有一行我们之前貌似没有见过box.castShadow = true。这个我们先留着,时机到了再说。

  • 添加一个平面,如果忘记了怎么添加平面的可以去three.js学习之平面图形圆环和平面复习一下,完整代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    const width = 40
    const height = 30
    const planeGeometry = new THREE.PlaneGeometry(width, height)
    const material = new THREE.MeshStandardMaterial({
    color: 0xffffff
    })
    const plane = new THREE.Mesh(planeGeometry, material)
    plane.position.set(0, 0,-10)
    plane.receiveShadow = true;
    group.add(plane)
    }

    这段代码中也有一部分对我们来说比较陌生plane.receiveShadow = true;

不出意外的话,我们得到了一个平面和一个红色的立方体,到了这里,除了偶尔看到立方体盒子变得明亮意外,貌似啥都看不到。接下来,咱们进入show time。正式开始光源的学习。

three.js light(光源)基础知识

在我们已经用了很多遍的基础代码中,有这样一段代码

1
2
3
4
5
6
7
{
const lightColor = 0xffffff; // 灯光颜色
const intensity = 1; // 灯光强度
const light = new THREE.DirectionalLight(lightColor, intensity);
light.position.set(10,1,20);
scene.add(light);
}

这就是three.js中光源部分的代码。我们来逐行看一下这些代码。

const lightColor = 0xffffff;用变量lightColor接收了一个16进制颜0xffffff白色。生活中的太阳光可以分解为七色光,照到不同的材料上根据材料的颜色会将对应的光线反射出来,因此我们能看到五颜六色的世界,白色也有点太阳光的那个意思。除了白色以外,我们也可以将灯光颜色设置成任意可用的颜色值。在three.js中,默认的灯光颜色就是白色,因此,我们通常也可以不设置灯光颜色。

const intensity = 1;用变量intensity接收了一个数值1,用来定义光照的强度。实际上如果我们需要的光照强度是1的话,大多数时候都不用去定义,因为默认值就是1。光照强度范围是大于0即可,小于等于0就相当于是黑夜,啥都看不见。

const light = new THREE.DirectionalLight(lightColor, intensity);通过构造函数THREE.DirectionalLight实例化了变量light,并且接收了已经定义好的灯光颜色lightColor变量和灯光强度intensity变量,使得光源能按照这两个变量的设置发出。

如果在构造的时候没有传入灯光颜色和强度,three.js就会使用默认颜色和强度补上。到了后面有需要时,我们也是可以随时去改变的。想要改变灯光颜色,可以使用内置的方法light.color.set(color),这里传入的color值,只要是合法的颜色值即可,不管是rgba,还是16进制,或者英文字母。而要想改变灯光强度,直接修改light.intensity属性即可。如下:

1
2
light.color.set('green') // 将灯光修改成绿色
light.intensity = 10 // 将灯光强度修改为1

light.position.set(10,1,20); 通过已经实例化变量light设置灯光的位置,通过设置position,我们可以决定光源从不同的位置发出光来。

至此,我们已经了解了three.js中光源的基础知识。后面我们将详细学习探索three.js中各种光源的特征和用处。

本节完整源代码如下,案例在https://www.91yqz.com/learnThree/learnThree15.html

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
<script>
function getSearchObj (key) {
const search = window.location.search.replace('?', '')
const list = search.split('&')
const obj = {}
for (let i = 0; i < list.length; i++) {
const eachList = list[i].split('=')
obj[eachList[0]] = eachList[1]
}
return obj[key]
}
async function main () {
const search = location.search
const canvas = document.querySelector('#three')
const renderer = new THREE.WebGLRenderer({canvas})
if (getSearchObj('shadowMap') === 'true') {
renderer.shadowMap.enabled = true;
}
const fov = 75;
const aspect = 2;
const near = 0.1;
const far = 200;

const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)

const group = new THREE.Group()
group.position.z = -10
const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, linecap:'round' })
const loader = new THREE.TextureLoader()
const load = (url) => {
return new Promise((resolve, reject) => {
loader.load(url, (texture) => {
resolve(texture)
})
})
}

{
const width = 4
const height = 4
const depth = 4
const boxGeometry = new THREE.BoxGeometry(width, height, depth)

const material = new THREE.MeshStandardMaterial({
color: 0xe74478
})

const box = new THREE.Mesh(boxGeometry, material)
box.position.set(0, 0, -5)
box.castShadow = true
group.add(box)
}

{
const width = 40
const height = 30
const planeGeometry = new THREE.PlaneGeometry(width, height)
const material = new THREE.MeshStandardMaterial({
color: 0xffffff
})
const plane = new THREE.Mesh(planeGeometry, material)
plane.position.set(0, 0,-10)
plane.receiveShadow = true;
// plane.rotation.x = -0.2 * Math.PI
group.add(plane)
}

const scene = new THREE.Scene()

{
const lightColor = '#ffffff'; // 灯光颜色
const intensity = 1; // 灯光强度
const light = new THREE.DirectionalLight(lightColor, intensity);
// light.position.set(10,1,20);
light.position.set(-1, 2, 4);
light.castShadow = true;
scene.add(light);

}
scene.add(group)

function resizeRenderSizeToDisplaySize () {
const pixelRatio = window.devicePixelRatio;
const displayWidth = canvas.clientWidth * pixelRatio
const displayHeight = canvas.clientHeight * pixelRatio
const renderWidth = canvas.width;
const renderHeight = canvas.height;
const needResize = displayWidth !== renderWidth || displayHeight !== renderHeight
return needResize
}
function render (time) {
if (resizeRenderSizeToDisplaySize()) {
const pixelRatio = window.devicePixelRatio;
const width = canvas.clientWidth * pixelRatio
const height = canvas.clientHeight * pixelRatio
renderer.setSize(width, height, false)
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}

time *= 0.001
group['children'].forEach((each, index) => {
if (index !== 1) {
each.rotation.x = -time
each.rotation.y = -time
}
})

// group.position.y = time
// group.position.z = time
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
// render()
}
main()
</script>