前言

近两年不管是在手机端还是PC端都能看到越来越多的炫酷的3D特效,比如淘宝前两年双十一就用ThreeJS做了一个比较酷炫的3D宣传页面刷爆了朋友圈。随着软硬件的的迅速发展,浏览器上进行web 3D开发已经具备基本条件了,随着web技术不断发展及衍生,出现了不少javascript 3d库。而我们今天将要揭秘的那些炫酷的3D特效,就是基于Three.js开发的,目前它已经发展为3d库中的佼佼者。

揭开神秘的Web 3D特效— Three.js 初识 - 图1

Three.js是什么?

当我们打开Three.js的官网,可以看到一句简单的介绍。“ three.js – JavaScript 3D library “,意思这是一个3D Javascript库,通过这个库我们可以很简单的通过WebGL编写3D程序,通过对 WebGL 接口的封装与简化而形成的一个易用的图形库。

上面我们又提到了WebGL,什么又是WebGL?WebGL 就是基于 OpenGL 设计的面向 web 的 3D 图形标准,它提供了一系列 JavaScript API,通过这些 API 进行图形渲染,系统硬件会加速 3D 渲染,从而获得较高性能。

而OpenGL 大概许多人都有所听闻,它是最常用的跨平台图形处理开源库。

WebGL 和 Three.js 区别

通过初步的了解,我们知道WebGL 和 Three.js 都可以进行 Web 端的 3D 图形开发。那问题来了,既然我们有了 WebGL,为什么还需要 Three.js?

  • WebGL 门槛相对较高
    • 解析几何
    • 线性代数
    • 矩阵运算
    • 各种绘图知识…
    • 计算机图形学需要相对较多的数学知识
  • Three.js 及其容易上手
    • 大大降低了学习成本
    • 简化了很多繁琐细节
    • 提供了非常友好的封装
    • 大大提升了开发效率
    • 并且没有损失WebGL 的灵活性
    • 针对 WebGL 提供非常友好的封装

因此,从 Three.js 入手是值得推荐的,这可以让你在较短的学习后就能面对大部分需求场景。就像很多人刚接触前端开发的初学者,当你还没有认真学习javascript基础语法的时候,如果你先接触了jQuery,在短期内就就会有种得心应手的感觉。

Web 3D 应用场景

有一句话叫:给我一个支点,我就能撬动地球。很多人都接触过2D,而3D最大的区别就是多了一根Z轴,而给我一个Z轴我就能创造3D世界。
那么3D给我们带来的是很强的视觉冲击,更加真实地感知世界,它会看起来更加的AMAZING。
那么让我们一起来了解下,Three.js可以用来做什么?可以做哪些让你觉得惊讶的产品。

3D 交互设计

淘宝造物节的3D活动页面,视觉更加的震撼。同时可以将文字、声音、视频和3D产品等多种媒体信息全面地展现,形成强烈的视觉冲击,更具有创意和美学动感。
揭开神秘的Web 3D特效— Three.js 初识 - 图2
3D 实景看房,720°全景空间展示,呈现每个角落的场景空间,模拟真实环境,有如身临其境的感觉。这种沉浸式的虚拟场景体验,能够应用在教育培训、娱乐、商业地产、旅游、产品展示、酒店介绍等场景体验。
揭开神秘的Web 3D特效— Three.js 初识 - 图3

3D 游戏开发

过去Flash+非标准插件的3D页面游戏模式,正在逐渐演变成由WebGL当家,现在WebGL为页面游戏带来的3D体验甚至可以媲美传统的PC桌面游戏。
揭开神秘的Web 3D特效— Three.js 初识 - 图4

3D 数据可视化

3D智慧城市数据可视化,360度立体视角进入城市,点击单个建筑能查看对应指标,核心数据包含有:人口、单位、建筑、车辆、轨迹、污染物、生态等。
揭开神秘的Web 3D特效— Three.js 初识 - 图5
商业大厦的人流量情况,游客情况,建筑硬件指标等展示清晰直观。
image.png

3D AR展示

可自动识别地面,点击召唤观赏AR汽车。可进行车身交互,点击按钮打开车灯、使车顶棚变透明,以观察内部等等。AR产品能够识别特定图像场景,即时交互,具有无限的创意。可广泛应用在广告、娱乐、产品展示、创新教育、定点巡查、视频展示等场景。
image.png

如何使用Three.js?

学习之前,我们先放出来一个整体拓扑图,方便大家对three.js整体有个初步的了解。
image.png

基本概念

如果想在屏幕上展示 3D 物体,大体上的思路是这样的:

  • 创建一个三维空间,Three.js 称之为场景( Scene )
  • 确定一个观察点,并设置观察的方向和角度,Three.js 称之为相机( Camera )
  • 在场景中添加供观察的物体,Three.js 中有很多种物体,如 Mesh、Group、Line 等,他们都继承自 Object3D 类。
  • 最后我们需要把所有的东西渲染到屏幕上,这就是 Three.js 中的 Renderer 的作用。

image.png

三大组件

场景 Sence

场景是所有物体的容器,场景只有一种。要构建一个场景也很简单,只要new一个对象就可以了,代码如下:

const scene = new THREE.Scene();
场景是所有物体的容器,如果要显示一个苹果,就需要将苹果对象加入场景中。

相机 Camera

另一个组建是相机,相机决定了场景中那个角度的景色会显示出来。相机就像人的眼睛一样,人站在不同位置,抬头或者低头都能够看到不同的景色。

场景只有一种,但是相机却又有很多种。和现实中一样,不同的相机确定了呈相像的各个方面。比如有的相机适合人像,有的相机适合风景,专业的摄影师根据实际用途不一样,选择不同的相机。对程序员来说,只要设置不同的相机参数,就能够让相机产生不一样的效果。常用的是透视摄像机和正交摄像机。
image.png

透视摄像机是最常用的摄像机类型,模拟人眼的视觉,近大远小(透视)。Fov表示的是视角,Fov越大,表示眼睛睁得越大,离得越远,看得更多。如果是需要模拟现实,基本都是用这个相机。

// 创建透视相机
const camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
1,
10000
);
image.png
透视摄像机呈现出来的近大远小的效果,比较符合现实。

image.png
正交摄像机是一个矩形可视区域,物体只有在这个区域内才是可见的物体无论距离摄像机是远或是近,物体都会被渲染成一个大小。一般应用场景是2.5d游戏如跳一跳、机械模型。

// 创建正交相机
const camera = new THREE.OrthographicCamera(
-window.innerWidth / 200,
window.innerWidth /200 ,
window.innerHeight/ 200,
-window.innerHeight/ 200,
1,
1000
);
image.png
可以看见上图的效果,有一个正方体已经走了很远但是大小不变。

渲染器 renderer

三大组件最后一个就是渲染器,渲染器的作用就是将相机拍摄出的画面在浏览器中呈现出来。渲染器决定了渲染的结果应该画在页面的什么元素上面,并且以怎样的方式来绘制。Three.js有很多种类的渲染器,例如webGLRenderer、canvasRenderer、SVGRenderer,通常使用的是webGLRenderer渲染器。

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
渲染器renderer的domElement元素,表示渲染器中的画布,所有的渲染都是画在domElement上的,所以这里的appendChild表示将这个domElement挂接在body下面,这样渲染的结果就能够在页面中显示了。

添加物体到场景

通过上面三大组件可以把3d世界画出来了,但是里面并没有什么东西。所以我们需要增加物体到场景,Three.js中有光源和各种mesh。

mesh

mesh即是网格。在计算机里,3D世界是由点组成的,无数的面拼接成各种形状的物体。这种模型叫做网格模型。一条线是两个点组成,一个面是3个点组成,一个物体由多个3点组成的面组成:
揭开神秘的Web 3D特效— Three.js 初识 - 图14
而网格(mesh)又是由几何体(geometry)和材质(material)构成的。

geometry

揭开神秘的Web 3D特效— Three.js 初识 - 图15
我们所能想象到的几何体,框架都自带了,我们只需要调用对应的几何体构造函数即可创建。几何体的创建方法都是new,如CubeGeometry:

var geometry = new THREE.CubeGeometry(1,1,1);
CubeGeometry到底是什么东西,其实他就是一个几何体,不过他到底是一个正方体还是长方体,是由它的3个参数所决定,CubeGeometry的原型如下代码所示:

CubeGeometry(width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides)
width:立方体x轴的长度
height:立方体y轴的长度
depth:立方体z轴的深度,也就是长度
想一想大家就明白,以上3个参数就能够确定一个立方体。
剩下的几个参数比较费解和复杂一些,我们现在也不用过多深入,不过之后我们会自己来写一个立方体,到时候,你会更明白这些参数的意义。

material

揭开神秘的Web 3D特效— Three.js 初识 - 图16
一个物体很多的物理性质,取决于其材料,材料也决定了几何体的外表。材料的创建方法也是new,如MeshBasicMaterial材料:

var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
MeshBasicMaterial是一种非常简单的材质,这种材质不考虑场景中光照的影响。使用这种材质的网格会被渲染成简单的平面多边形,而且也可以显示几何体的线框。
MeshBasicMaterial 可选参数一般指定为颜色,{color:0xffffff},也可以添加别的从Material父类继承来的属性。除却继承的属性外,MeshBasicMaterial也有自己独有的属性,以下是部分属性。

好了,现在已经有了geometry和material,就可以创建一个mesh并追加到场景中:

var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

外部模型

现实世界丰富多彩,不是Threejs的几种默认几何形状和材质所能表达的,实际运用中,很多时候需要用到外部模型,通过3D建模软件构建物体的三维模型并导出模型文件,Threejs导入模型文件进行使用。
揭开神秘的Web 3D特效— Three.js 初识 - 图17

设置光源

一个3d世界,如果需要更加逼真,那就需要光源了。光也有很多种:

  • 环境光 AmbientLight:它的颜色会添加到整个场景和所有对象的当前颜色上
  • 点光源 PointLight:这种光源放出的光线来自同一点,且辐射方向四面八方,如蜡烛发出的光
  • 方向光 DirectionalLight :也称作无限光,从这种光源发出的光线可以看做是平行的,如太阳光
  • 聚光灯 SpotLight :这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果,如手电筒发出的光。

光源的创建,如方向光:

const light = new THREE.DirectionalLight(0xffffff, 0.9)
在Three.js中,能形成阴影的光源只有DirectionalLight和SpotLight;而相对地,能表现阴影效果的材质只有LambertMaterial和PhongMaterial。Three.js中渲染阴影的开销比较大,所以默认物体是没有阴影的,需要单独开启。

代码实现

好了,前面说了这么多,总该写代码了~~根据上面说的,我们首先需要创建一个场景( Scene )、相机( Camera )、渲染器( Renderer )。

// 创建场景
var scene = new THREE.Scene();
// 创建透视相机
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
// 创建一个 WebGL 渲染器
var renderer = new THREE.WebGLRenderer({
alpha: true,// 默认情况下为黑色场景,此处设置为透明(即白色场景)
});
// 设置渲染器为全屏
renderer.setSize(window.innerWidth, window.innerHeight);
// 添加到网页中
document.body.appendChild(renderer.domElement);
当了解图形的基本知识之后,在上面的代码的基础上添加简单图像。

var geometry = new THREE.BoxGeometry(1,1,1); // 创建一个长宽高都为1个单位的立方体
var material = new THREE.MeshBasicMaterial({color: 0x00ff00}); // 创建材质,对光照无感
var cube = new THREE.Mesh(geometry, material); // 创建一个立方体网格(mesh),将材质包裹在立方体上
scene.add(cube); // 将立方体网格添加到场景中
camera.position.z = 5; // 指定相机位置
function render() {
requestAnimationFrame(render); // 让浏览器执行参数中的函数,不断循环(浏览器一个新的API)
renderer.render(scene, camera); // 结合场景和相机进行渲染,即用摄像机拍下此刻的场景
}
render();
在render()函数中添加以下代码使上面在场景中添加的正方体运动起来。

cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
通过以上代码的实现,我可以在浏览器中看到一个正方形正在旋转,如下图:
揭开神秘的Web 3D特效— Three.js 初识 - 图18

通过这篇文章我们对web 3d特效有了初步的认识,并且基于three.js完成了一个简单的示例开发。随便web 3d的广泛应用, 后续还会有更加深入的学习,期待下次与大家分享学习成功。