Three.js 是一个流行的 WebGL 封装,我和它初次见面貌似是在 Google 的 WebGL Globe , 那是一个用来展(xuan)示(yao) Chrome 浏览器牛逼功能的页面——当时 Chrome 在对 WebGL 的 支持上可谓是一枝独秀, Firefox 什么的完全不够看……当然现在的情况大不一样了,随着 WebGL 的普及,在 Web 页面上显示 3D 内容也在不断地实用化。我今天就来看看传说中最实用的 Three.js.

基本概念

基本类型

Three.js 中的基本类型是 Object3D 类,这个类代表可以在 3D 空间中进行位移、旋转、缩放等操作 的实体,主要作用就是处理这些 3D 实体的 变换矩阵 ,让它们以正确的大小和姿态出现在正确的 位置。

Object3D 对象之间具有树状的层级关系, parent 的变换矩阵会应用到 children 上,而 children 的矩阵不会对 parent 产生影响。 举个例子,汽车车身在摆动的时候轮子会跟着动,但是轮子转动时 却不会带动车身,这里就可以将车身看作 parent ,四个轮子看作 children.

场景和视角

3D 模型的渲染过程需要两个要素:要被渲染的 3D 模型数据、观察者的位置以及角度。这两个要素在 Three.js 里被抽象为 Scene 类和 Camera 类,它们都是 Object3D 的子类,所以同样可以被各种 移动、旋转、缩放。在最简单的情况下,我们只需要初始化一个 Scene 和一个 Camera:

1
2
3
4
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(5, 5, 5);
camera.lookAt(new THREE.Vector3(0, 0, 0));

PerspectiveCamera 是 Camera 的子类,带有一个透视变换矩阵,所以通过它渲染出来的图像是带透 视效果的,就像我们平时看到的东西一样,近大远小。各个参数的含义嘛,见文档……

这里我们把 camera 放在 XYZ 坐标为 (5, 5, 5) 的位置上,并且朝向坐标原点。

Renderer

接下来,只要将 scene 和 camera 交给 Three.js 的 Renderer ,渲染操作就水到渠成了:

1
2
3
4
5
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 1);
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);

顾名思义, WebGLRenderer 就是用 WebGL 在做渲染操作;貌似 Three.js 还支持一个 CanvasRenderer , 但限制颇多……

无论是哪种 Renderer ,都有一个 domElement 属性,这其实就是个 canvas ,你可以把它放到 HTML 文档流的任意合法位置。

画出坐标轴

Three.js 中有不同的数据类型,用于描述点、线、面、体等几何结构。描述线的类型嘛, 自然就叫 Line 啦。三维空间中的线条可以由一组三维空间中的点来定义,但是这些线条 除了位置等几何信息,还具有颜色等用于渲染的其他信息。

Three.js 使用 Geometry 类型描述几何结构,同时使用 Material 类型描述材质等用于渲 染过程的信息,所以画线的时候,我们需要分别声明线条的几何结构和材质信息:

 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
function drawCoords(scene) {
    var xmat = new THREE.LineBasicMaterial({color: 0xff0000});
    var ymat = new THREE.LineBasicMaterial({color: 0x00ff00});
    var zmat = new THREE.LineBasicMaterial({color: 0x0000ff});

    var xgeo = new THREE.Geometry();
    xgeo.vertices.push(
        new THREE.Vector3(-5, 0, 0),
        new THREE.Vector3(5, 0, 0),
        new THREE.Vector3(4.8, 0.2, 0)
    );
    var ygeo = new THREE.Geometry();
    ygeo.vertices.push(
        new THREE.Vector3(0, -5, 0),
        new THREE.Vector3(0, 5, 0),
        new THREE.Vector3(0.2, 4.8, 0)
    );
    var zgeo = new THREE.Geometry();
    zgeo.vertices.push(
        new THREE.Vector3(0, 0, -5),
        new THREE.Vector3(0, 0, 5),
        new THREE.Vector3(0, 0.2, 4.8)
    );

    var xline = new THREE.Line(xgeo, xmat);
    var yline = new THREE.Line(ygeo, ymat);
    var zline = new THREE.Line(zgeo, zmat);

    scene.add(xline);
    scene.add(yline);
    scene.add(zline);
}

这里的 xmat、ymat、zmat 分别描述了 X、Y、Z 轴的材质,而 xgeo、ygeo、zgeo 则是 几何结构。 Geometry 对象的 vertices 属性包含了所有的顶点位置,由以上代码可见, 对于 Line 而言,我们只需要往几何信息里 push 一系列的点(以 Vector 类型描述) 就足以描述这条 Line 了, Three.js 在渲染 Line 的时候会自动将这些点按顺序用直 线连接起来。

得到的结果

下面是程序执行的结果,当然你的浏览器需要支持 WebGL.