logo头像
Snippet 博客主题

threejs入门

前言

随着HTML5的发布,我们可以通过WebGL在浏览器上直接使用显卡资源来创建高性能的二维和三维图形,但是直接使用WebGL编程来创建三维场景十分复杂而且还容易出问题。而使用Three.js库可以简化这个过程,它对WebGL做了进一步封装,降低了使用门槛。WebGL整体原理其实和OpenGL差不多,只是运行平台不同而已,它是基于OpenGL-ES绑定javascript而来。

底层关系: OpenGL ES => WebGL => Threejs

注: 本系列教程并不是用的threejs最新版,threejs官方一直在持续优化更新,可能有些API使用有些许变化。本系列教程重在学习理论,只有掌握这些后,后续关注官网最新版使用才能如鱼得水。
threejs官网: 点击这里


环境搭建

three.js是一个javascript库,所以环境搭建和我们web开发环境搭建一样。

  1. 编码IDE,我用的vscode, 下载地址

  2. 搭建web服务器。

    方式有很多,我用的nodejs的http-server模块。

    1
    2
    3
    4
    5
    // 全局安装
    npm install -g http-server

    // 启动服务
    http-server
  3. 访问示例程序。

    对应示例程序目录下启动http-server,默认端口是8080。浏览器访问url,如:

    localhost:8080/chapter-01.html


入门示例程序

入门示例我们将使用three.js库创建如下主要内容:

  1. html页面如何引入three.js库。
  2. 使用three.js创建场景、摄像机、光源、渲染物体。
  3. 给场景添加阴影和动画效果。
  4. 添加辅助库dat.GUI和stats.js 创建用户控制界面(动态调渲染参数)和场景渲染时的帧数。

本系列所有示例程序引用的外部库和资源下载地址:示例程序资源下载

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
160
161
162
163
164
<!-- chapter-01.html -->
<!DOCTYPE html>

<html>

<head>
<title>入门示例</title>
<!-- 引入three.js等相关库,three.js库引入有两个版本,这里为了学习引入的是没有压缩的版本,还一个three.min.js是压缩过的,只有three.js的四分之一大小,一般用于发布版本 -->
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>

<body>

<div id="Stats-output">
</div>

<div id="WebGL-output">
</div>

<script type="text/javascript">
var camera;
var scene;
var renderer;

function init() {
// 初始化fps显示
var stats = initStats();

// 创建场景
scene = new THREE.Scene();

// 创建摄像机
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

// 创建渲染器
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0)); // 设置背景颜色
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true; // 开启阴影(默认是关闭的,因为它比较耗计算资源)

// 创建平面
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff}); // 物体受光源影响和使用的材质有关系,如果是MeshBasicMaterial基本材质,不会对光源有任何反应,只会使用指定的颜色来渲染物体。MeshLambertMaterial和MeshPhongMaterial材质在渲染时会对光源产生反应,没有光它就是漆黑的。
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true; // 开启接受阴影

// 旋转和设置平面位置
plane.rotation.x = -0.5 * Math.PI; // 绕x轴旋转90度
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;

// 将平面添加到场景容器中
scene.add(plane);

// 创建立方体
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true; // 开启投射阴影

// 同上
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);

// 创建球体
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;

// 同上
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
scene.add(sphere);

// 设置摄像机位置
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);

// 添加光源
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true; // 开启投射阴影
scene.add(spotLight);

// 把渲染器对象添加到div中显示
document.getElementById("WebGL-output").appendChild(renderer.domElement);

// 设置需要动态调参的参数
var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
};

// dat.GUI是Google员工开发的动态调参数的插件,这样就大大方便了我们调整相关渲染参数的操作了
var gui = new dat.GUI();
gui.add(controls, 'rotationSpeed', 0, 0.5); // 参数对象、参数名、参数的取值范围
gui.add(controls, 'bouncingSpeed', 0, 0.5);
render();

// 渲染场景
var step = 0;
function render() {
// 更新fps
stats.update();

// 旋转立方体的各个面
cube.rotation.x += controls.rotationSpeed;
cube.rotation.y += controls.rotationSpeed;
cube.rotation.z += controls.rotationSpeed;

// 球体跳动
step += controls.bouncingSpeed;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));

// 重新渲染场景
requestAnimationFrame(render);
renderer.render(scene, camera);
}

// 初始化fps显示
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: 渲染时间ms

stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}

// 窗口大小发生变化后重新适配渲染场景
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

// 监听窗口大小发生变化事件
window.addEventListener('resize', onResize, false);

// 页面加载完成后调用init方法
window.onload = init;

</script>
</body>
</html>

显示效果: