在hello three.js中,在默认画布上我们做出了一个小的立方体。默认情况下,three.js
中的画布大小是300 * 200,所以我们做出来的东西,在大大的屏幕上也只占小小的一块。在实际开发中,我们可能需要根据实际情况去调整画布的大小,最常见的应该就是让画布满屏了。
这一节当中,我们会做两件事,第一,给应用添加灯光,只需要知道怎么添加就行,不会详细讲;第二,实现three.js
的响应式开发。
在正式开始之前,我们首先在learnThree
文件夹下新建一个responseThree.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<!DOCTYPE html>
<html>
<head>
<title>threejs响应式设计开发</title>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r99/three.min.js"></script>
</head>
<body>
<canvas id="three"></canvas>
<script>
function main () {
const canvas = document.querySelector('#three')
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // 默认值
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
const material = new THREE.MeshBasicMaterial({color: 0x44aa88});
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const cube = new THREE.Mesh(geometry, material);
cube.position.z = -2
const scene = new THREE.Scene();
scene.add(cube)
function render (time) {
time *= 0.001
cube.rotation.x = time
cube.rotation.y = time
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
}
window.onload = main
</script>
</body>
</html>
添加灯光
在浏览器中打开responseThree.html
,我们虽然能够是一个立方体在旋转,但还是不够清晰,因此我们先将light
灯光给它放进去,然后再继续。
- 创造灯光平行光会将灯光射向原点,但是它自己默认为止也在原点,因此咱们将等的位置移动到立方体左上方一点。然后添加到整个场景中去。
1
2
3
4
5const lightColor = 0xffffff; // 灯光颜色
const intensity = 1; // 灯光强度
const light = new THREE.DirectionalLight(lightColor, intensity);
light.position.set(-1, 2, 4);
scene.add(light); 改变材质
就跟在现实中差不多,有的东西反光,有的则不反,之前咱们使用的MeshBasicMaterial
材料就不反光,所以我们给他换成反光的MeshPhongMaterial
材质:1
2
3const material = new THREE.MeshBasicMaterial({color: 0x44aa88});
改成
const material = new THREE.MeshPhongMaterial({color: 0x44aa88});完整代码如下
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<!DOCTYPE html>
<html>
<head>
<title>threejs响应式设计开发</title>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r99/three.min.js"></script>
</head>
<body>
<canvas id="three"></canvas>
<script>
function main () {
const canvas = document.querySelector('#three')
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // 默认值
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
const material = new THREE.MeshPhongMaterial({color: 0x44aa88});
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const cube = new THREE.Mesh(geometry, material);
cube.position.z = -2
const scene = new THREE.Scene();
scene.add(cube)
{
const lightColor = 0xffffff; // 灯光颜色
const intensity = 1; // 灯光强度
const light = new THREE.DirectionalLight(lightColor, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
function render (time) {
time *= 0.001
cube.rotation.x = time
cube.rotation.y = time
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
}
window.onload = main
</script>
</body>
</html>现在,可以看到立方体转到不同的面时,明暗程度不同。
响应式开发
将html,body的margin设置为0,宽高设置为100%。canvas设置为block,宽高100%。
1 | html,body{ |
这时刷新页面,变换窗口的大小,可以看到两个问题,一个是正方体虚化,边缘有很多锯齿状的东西,第二个是在变化窗口大小的过程中,正方体会变形。接下来我们来解决这两个问题。
解决three.js中边缘锯齿状虚化问题
平时的开发中,我们知道图片有两个尺寸,一个是图片自己的原始尺寸,一个是我们用css设置的图片显示尺寸。如果显示尺寸的大小大于原始图片尺寸,图片就会出现虚化的问题。同理,canvas也是这样,当canvas的原始尺寸小于显示尺寸的时候,就会出现虚化锯齿的问题。
为了解决three.js中物体边缘虚化的问题,我们可以实时对canvas的实际大小和显示大小进行对比,当二者不一致时,我们进行调整就可以。
- 首先,我们增加监测canvas实际大小和显示大小的函数写出来。
1
2
3
4
5
6
7
8
9function resizeRenderSizeToDisplaySize (canvas) {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
const renderWidth = canvas.width;
const renderHeight = canvas.height;
const needResize = displayWidth !== renderWidth || displayHeight !== renderHeight
return needResize
} 调整canvas实际大小和显示大小一致,消除锯齿
使用three.js内置的方法
renderer.setSize(width, height,updateStyle)
方法。renderer.setSize
需要传入三个参数,分别是宽,高和是否根据传入的宽高改变canvas的显示样式。这里我们肯定是不希望它去改变显示样式的,因此第三个值默认传入false
就可以了。代码如下:
1
2
3if (resizeRenderSizeToDisplaySize(canvas)) {
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false)
}完整代码如下:
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<!DOCTYPE html>
<html>
<head>
<title>threejs响应式设计开发</title>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r99/three.min.js"></script>
</head>
<body>
<canvas id="three"></canvas>
<script>
function main () {
const canvas = document.querySelector('#three')
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // 默认值
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
const material = new THREE.MeshPhongMaterial({color: 0x44aa88});
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const cube = new THREE.Mesh(geometry, material);
cube.position.z = -2
const scene = new THREE.Scene();
scene.add(cube)
{
const lightColor = 0xffffff; // 灯光颜色
const intensity = 1; // 灯光强度
const light = new THREE.DirectionalLight(lightColor, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
function render (time) {
if (resizeRenderSizeToDisplaySize(canvas)) {
render.setSize(canvas.clientWidth, canvas.clientHeight, false)
}
time *= 0.001
cube.rotation.x = time
cube.rotation.y = time
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
}
function resizeRenderSizeToDisplaySize (canvas) {
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
const renderWidth = canvas.width;
const renderHeight = canvas.height;
const needResize = displayWidth !== renderWidth || displayHeight !== renderHeight
return needResize
}
window.onload = main
</script>
</body>
</html>
这个时候刷新页面,你就能看到锯齿消失了,实体更清晰了。
- 解决three.js中变形的问题
在解决锯齿状的问题中,我们是通过改变显示大小和canvas的实际大小来实现的。同理,变形的问题,也是由于实际的宽高比例和显示的宽高比例不同导致的,因此我们只需要在调整宽高比例的时候调整相机显示的宽高比与canvas的宽高比一致即可。如下:首先我们手动调整aspect为实际canvas的宽高比,接着我们用1
2camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();updateProjectionMatrix()
方法告诉three.js我们的相机参数更新了
现在,无论怎们调整浏览器显示窗口大小,立方体都不会变形了。为了节省篇幅,我决定以后不在每一步都贴出代码了,只在每篇结尾将完整代码贴出来,如果需要看完整代码,请转文末。
适配视网膜屏幕
我们知道,在视网膜屏幕中,至少都是2倍或者3倍像素,为了适配视网膜屏幕,我们有两个方法。
- 使用three自带的
renderer.setPixelRatio(pixel)
方法将当前设备的像素比传进去,后面的事由three.js
自己完成。
代码如下:1
renderer.setPixelRatio(window.devicePixelRatio);
这样,后面所有的renderer.setSize
都会自动乘以传入的window.devicePixelRatio
。
另一个方法是在调整canvas大小的时候
首先改变判断实际显示的canvas和canvas实际大小的函数1
2
3
4
5
6
7
8
9
10function resizeRenderSizeToDisplaySize (canvas) {
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
}然后改变改变canvas大小的代码
1
2
3
4if (resizeRenderSizeToDisplaySize(canvas)) {
const pixelRatio = window.devicePixelRatio;
renderer.setSize(canvas.clientWidth * pixelRatio, canvas.clientHeight * pixelRatio, false)
}
刷新看一下,可能改变不太大。
最后附上完整代码和我实现的案例
1 | <!DOCTYPE html> |