在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
 3- const 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
 3- if (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
 10- function 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
 4- if (resizeRenderSizeToDisplaySize(canvas)) { 
 const pixelRatio = window.devicePixelRatio;
 renderer.setSize(canvas.clientWidth * pixelRatio, canvas.clientHeight * pixelRatio, false)
 }
刷新看一下,可能改变不太大。
最后附上完整代码和我实现的案例
| 1 | <!DOCTYPE html> |