本文主要是讲解 Three.js 的相关概念,帮助大家对 Three.js 以及相关知识形成比较完整的理解。
今年来 web 得到了快速的发展,随着 HTML5 的普及,网页的表现能力越来越大。网页上已经可以做出很多复杂的动画,精美的效果。
在学习 Three.js 之前,让我们先了解一下OpenGL和WebGL。
OpenGL
OpenGL 大概许多人都有所耳闻,它是最常用的跨平台图形库。
WebGL
WebGL 是基于 OpenGL 设计的面向 web 的图形标准,提供了一系列 JavaScript API,通过这些 API 进行图形渲染得以利用图形硬件从而获得较高性能。
Three
Three 是通过对 WebGL 接口的封装与简化而形成的一个易用的图形库。
简单点的说法:WebGL 可以看成是浏览器给我们提供的接口,在 javascript 中可以直接用这些 API 进行 3D 图形的绘制;而 Three.js 就是在这些接口上又帮我们封装得更好用一些。
准备工作
开发环境
Three.js 是一个 javascript 库,所以,我们可以使用平时开发 Javascript 应用的环境开发 Three.js 应用。
调试建议使用 Chrome 或者 Firefox 浏览器。如果你使用的是 Firefox,那么Firebug 会是你必不可少的插件;如果你使用的是 Chrome,那么直接使用控制台调试即可。这些和 Javascript 的调试是相同的。
下载
首先,我们需要在 Github 下载 Three.js 的代码。
下载,我们可以在源码中,看到 three.js 和 three.min.js 两个文件,前者是没有经过代码压缩的,因此适用于调试阶段;后者是经过代码压缩的,调试起来会不太方便,但文件较小,适用于最终的发布版。保存一个文件到本地,这里我们选择 three.js。
引用
在使用 Three.js 之前,我们需要在 HTML 文件中引用该文件:
<script type="text/javascript" src="three.js"></script>
然后就能通过全局变量THREE访问到所有属性和方法了。
Hello,world!
每一个新技术的学习,我们都是从 hello,world 开始的,学习 three.js 我们也不例外。
WebGL 的渲染是需要 HTML5 Canvas元素的,我们可以手动在 HTML 的<body>部分中定义 Canvas 元素,或者让 Three.js 帮你生成。这两种选择一般没有多大差别。
<body onload="init()">
<canvas id="mainCanvas" width="400px" height="300px" ></canvas>
</body>
在JavaScript代码中定义一个init函数,在HTML加载完后执行:
function init() {
// ...
}
一个典型的 Three.js 程序至少要包括渲染器(Renderer)、场景(Scene)、照相机(Camera),以及你在场景中创建的物体。这里我们将介绍如何快速地使用这些东西。
渲染器(Renderer)
渲染器将和 Canvas 元素进行绑定,如果之前在 HTML 中手动定义了 id 为 mainCanvas 的 Canvas 元素,那么 Renderer 可以这样写:
var renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('mainCanvas');
});
而如果想要使用 Three.js 生成 Canvas 元素,在 HTML 中就不需要定义 Canvas 元素,在 Javascript 代码中可以这样写:
var renderer = new THREE.WebGLRenderer();
renderer.setSize(400, 300);
document.getElementByTagName('body')[0].appendChild(renderer.domElement);
上面代码的第二行表示设置 Canvas 的宽 400 像素,高 300 像素。第三行将渲染器对应的 Canvas 元素添加到<body>中。
我们可以使用下面的代码将背景色(用于清除画面的颜色)设置为黑色:
renderer.setClearColor(0x000000);
场景(Scene)
在 Three.js 中添加的物体都是添加到场景中的,因此它相当于一个大容器。一般说,场景里没有很复杂的操作,在程序最开始的时候进行实例化,然后将物体添加到场景中即可。
var scene = new THREE.Scene();
照相机(Camera)
在介绍照相机设置前,我们先来简单了解下坐标系。WebGL 和 Three.js 使用的坐标系是右手坐标系,看起来就是这样的:
这里,我们定义了一个透视投影的照相机。
var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);
camera.position.set(0, 0, 5);
scene.add(camera);
值得注意的是,照相机也需要被添加到场景中。
长方体
我们要创建一个x、y、z方向长度分别为1、2、3的长方体,并将其设置为红色。
var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
new THREE.MeshBasicMaterial({
color: 0xff0000
})
);
scene.add(cube);
这段代码也是比较容易理解的,虽然现在可能还不知道 MeshBasicMaterial 是什么,但是大致可以猜测出这是一种材质,可以用来设置物体的颜色。还是要提醒下,一定要记得把创建好的长方体添加到场景中。
那么这里长度为1的单位是什么呢?这里的长度是在物体坐标系中的,其单位与屏幕分辨率等无关,简单地说,它就是一个虚拟空间的坐标系,1代表多少并没有实际的意义,而重要的是相对长度。
渲染
在定义了场景中的物体,设置好的照相机之后,渲染器就知道如何渲染出二维的结果了。这时候,我们只需要调用渲染器的渲染函数,就能使其渲染一次了。
renderer.render(scene, camera);
渲染效果图:
OK,我们前面已经简单快速的讲了一下如何快速搭建,下面让我们来讲下 Three.js 中的一些概念:
我们要在屏幕上展示 3D 图形,思路大体上是这样子的。
构建一个三维控件:
1、Three 中称之为场景(Scene)选择一个观察点,并确定观察方向/角度等 ;
2、Three 中称之为相机(Camera)在场景中添加供观察的物体 ;
3、Three 中的物体有很多种,包括 Mesh, Line, Points等,它们都继承自 Object3D 类将观察到的场景渲染到屏幕上的指定区域 ;
4、Three 中使用 Renderer 完成这一工作。
Scene
场景是所有物体的容器,也对应着我们创建的三维世界。
Camera 坐标系
Camera 是三维世界中的观察者,为了观察这个世界,首先我们要描述空间中的位置。
三维投影
Three 中的相机有两种,分别是正交投影相机 THREE.OrthographicCamera 和透视投影相机 HREE.PerspectiveCamera。
正交投影与透视投影的区别如上图所示,左图是正交投影,物体发出的光平行地投射到屏幕上,远近的方块都是一样大的;右图是透视投影,近大远小,符合我们平时看东西的感觉。
正交投影相机
注:图中的”视点”对应着 Three 中的 Camera。
这里补充一个视景体的概念:视景体是一个几何体,只有视景体内的物体才会被我们看到,视景体之外的物体将被裁剪掉。这是为了去除不必要的运算。
正交投影相机的视景体是一个长方体,OrthographicCamera 的构造函数是这样的:OrthographicCamera( left, right, top, bottom, near, far )
Camera本身可以看作是一个点,left 则表示左平面在左右方向上与 Camera 的距离。另外几个参数同理。于是六个参数分别定义了视景体六个面的位置。
可以近似地认为,视景体里的物体平行投影到近平面上,然后近平面上的图像被渲染到屏幕上。
透视投影相机
透视投影相机的视景体是个四棱台,它的构造函数是这样的:PerspectiveCamera( fov, aspect, near, far )
fov 对应着图中的视角,是上下两面的夹角。aspect 是近平面的宽高比。在加上近平面距离 near,远平面距离 far,就可以唯一确定这个视景体了。
透视投影相机很符合我们通常的看东西的感觉,因此大多数情况下我们都是用透视投影相机展示3D效果。
Objects
有了相机,总需要看点什么吧?这个时候我们就可以在场景中添加一些物体了。
Three 中供显示的物体有很多,它们都继承自 Object3D 类,这里我们主要看一下 Mesh 和 Points 两类。
Mesh
我们都知道,计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。线段很多时,看起来就是一条平滑的弧线了。
计算机中的三维模型也是类似的,普遍的做法是用三角形组成的网格来描述,我们把这种模型称之为Mesh模型。
这是那只著名的斯坦福兔子。它在 3D 图形中的地位与数字图像处理领域中著名的 lena 是类似的。
看这只兔子,随着三角形数量的增加,它的表面越来越平滑/准确。
在 Three 中,Mesh 的构造函数是这样的:Mesh( geometry, material )
geometry 是它的形状,material 是它的材质。
不止是 Mesh,创建很多物体都要用到这两个属性。下面我们来看看这两个重要的属性。
Geometry
Geometry 形状,相当直观。Geometry 通过存储模型用到的点集和点间关系(哪些点构成一个三角形)来达到描述物体形状的目的。
Three 提供了立方体(其实是长方体)、平面(其实是长方形)、球体、圆形、圆柱、圆台等许多基本形状;
我们也可以通过自己定义每个点的位置来构造形状;
对于比较复杂的形状,我们还可以通过外部的模型文件导入。
Material
Material,材质,这就没有形状那么直观了。
材质其实是物体表面除了形状以为所有可视属性的集合,例如色彩、纹理、光滑度、透明度、反射率、折射率、发光度。
这里讲一下材质(Material)、贴图(Map)和纹理(Texture)的关系。
材质上面已经提到了,它包括了贴图以及其它。
贴图其实是‘贴'和‘图',它包括了图片和图片应当贴到什么位置。
纹理嘛,其实就是‘图'了。
Three 提供了多种材质可供选择,能够自由地选择漫反射/镜面反射等材质。
Points
讲完了Mesh,我们来看看另一种Object——Points。
Points其实就是一堆点的集合,它在之前很长时间都被称为ParticleSystem(粒子系统),r68版本时更名为PointCloud,r72版本时才更名为Points。更名主要是因为,Mr.doob认为,粒子系统应当是包括粒子和相关的物理特性的处理的一套完整体系,而Three中的Points简单得多。因此最终这个类被命名为Points。
Points能够用来实现的典型效果是这样的:官方example
Light
神说:要有光!
光影效果是让画面丰富的重要因素。
Three 提供了包括环境光 AmbientLight、点光源 PointLight、 聚光灯 SpotLight、方向光 DirectionalLight、半球光HemisphereLight 等多种光源。
只要在场景中添加需要的光源就好了。
Renderer
在场景中建立了各种物体,也有了光,还有观察物体的相机,是时候把看到的东西渲染到屏幕上了。这就是Render做的事情了。
Renderer绑定一个canvas对象,并可以设置大小,默认背景颜色等属性。
调用Renderer的render函数,传入scene和camera,就可以把图像渲染到canvas中了。