![WebGL开发与应用](https://wfqqreader-1252317822.image.myqcloud.com/cover/712/31729712/b_31729712.jpg)
2.1 Three.js引擎中的基本概念
2.1.1 三维坐标系
三维坐标系是进行Web3D开发的基础(图2-1中,红、绿、蓝三条射线分别代表了x、y、z轴),在Three.js中,三维坐标系统如下(右手系):
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00018001.jpg?sign=1739010708-YEhOSna0EIyXTrP7020S75NjLB1du5Yp-0-dfc1b32b890b2a8f88600657b8d1f74f)
图2-1 三维坐标系
x轴:水平向右;
y轴:垂直向上;
z轴:垂直与屏幕向外;
原点:画布中心,即坐标(0,0,0)。
举例来说,图2-1中空心圆饼的坐标是(10,0,0),空心圆环的坐标是(-5,0,-5),此处的数值单位本身意义并不大,它只是表示了一个相对位置,但通常用米做单位可以达到最好的效果,尤其是在从建模工具导出模型到Three.js时。
需要注意的是,这样的坐标系统与大多数的三维建模软件(如3Dmax、Revit)都是不同的,它们一般在水平面上使用x轴和y轴,在垂直方向上使用z轴,因此,从这些建模软件中导出模型到Three.js中时,要进行y轴和z轴的变换。
2.1.2 透视摄像机
摄像机定义了三维空间到二维屏幕的投影方式,用“摄像机”这样一个类比,可以使我们直观地理解这一投影方式,它反映了在一个3D场景中哪部分内容可以显示在用户画布上。
一个典型的透视摄像机(Perspective Camera)包含4个参数,比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019001.jpg?sign=1739010708-76FyqjJg85V85p5CPmcB4Ksj556gA5nv-0-8d1c1a66168bb9392cb8d3b2f7a39ab2)
其中,fov代表摄像机的视野广度;w/h代表摄像机的宽高比,实践中一般设置为画布本身的宽高比;near代表近视点,小于这个距离的对象不能显示;far代表远视点,大于这个距离的对象不能显示。通过改变摄像机的位置、朝向和角度,即可改变画布中的内容,比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019002.jpg?sign=1739010708-hA6vm0YCqRLcIgNQVIoZ2IQx9nUUytck-0-ecfe779aea0316f474febb622e364483)
这两行代码的意思是将摄像机置于画布正外侧5 m的位置上,朝向原点。
2.1.3 正交投影摄像机
使用透视摄像机获得的结果类似于人眼在真实世界中所看到的效果(近大远小),如图2-2所示;而使用正交投影摄像机获得的结果则像在几何课上所画的效果,对于在三维空间内平行的线,投影到二维空间中也一定是平行的,如图2-3所示。一般说来,对于制图、建模软件通常使用正交投影,这样不会因为投影而改变物体比例;而对于其他大多数应用通常使用透视投影,因为这更接近人眼的观察效果。当然,摄像机的选择并没有对错之分,可以根据应用的特性选择一个效果更佳的摄像机。
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019003.jpg?sign=1739010708-lWYtF0IPLyQzInKL8bGxi7ZOkIdMuKh0-0-7bcf9bdbeb774ae0cc3ef5f594410f1b)
图2-2 透视摄像机
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019004.jpg?sign=1739010708-9HaPkjtSM6QFnVzedBP4UkNDhoqGSSh7-0-493d9e0cc03d41e989dba5c28979b190)
图2-3 三维空间内平行的线
正交投影摄像机(Orthographic Camera)设置起来较为直观,它的构造函数是:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019005.jpg?sign=1739010708-sr5TdkvYaJwkYDJr6FxiyFBTgCqg2bW0-0-57500223eb6df0ced43592fbe19c16d6)
这6个参数分别代表正交投影摄像机拍摄到的空间的6个面的位置,这6个面围成一个长方体,我们称其为视景体(Frustum),如图2-4所示。只有在视景体内部的物体才可能显示在屏幕上,而视景体外的物体会在显示之前被裁剪掉。
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019006.jpg?sign=1739010708-QGEfrTgufy7Fs8RfCzFkUZxBZI6aMUjK-0-6e9965babbd35407b106e14390b92db1)
图2-4 正交投影摄像机视景体
2.1.4 基本3D元素
1.场景
场景(Scene)就是一个三维空间,任何3D对象都必须被加入到场景中才能显示,一个典型的场景如scene=new THREE.Scene();。
2.渲染器
显示在用户屏幕上的内容最终是靠渲染器(Renderer)渲染出来的,该对象封装了核心的WebGL原生API,一个典型的渲染器如renderer=new THREE.WebGLRenderer({antialias:true});。
3.灯光
在WebGL的三维空间中,存在点光源和聚光灯两种类型,而且,作为点光源的一种特例还存在平行光源(无限远光源),作为光源的参数还可以进行环境光的设置。作为对应,Three.js中可以设置点光源(Point Light)、聚光灯(Spot Light)、平行光源(Direction Light)和环境光(Ambient Light)。在一个场景中可以设置多个光源,基本上都会是环境光和其他几种光源进行组合。如果不设置环境光,那么光线照射不到的面会变得过于黑暗。一些典型的光源如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00020001.jpg?sign=1739010708-p71yewWcpYHCYexdixcKspQNCNYgl074-0-15718f8497827de0e300df02707cc961)
4.网格
网格(Mesh)是Three.js中最重要、最常用的一种3D对象,绝大多数情况下,使用该对象来完成3D场景的布置,一个网格对象需要两个初始化参数,分别是几何(geometry)和材质(material),比如:var mesh=new THREE .Mesh(geometry,material)。
5.几何
几何(Geometry)对象定义一个3D对象的几何形状。一些基本的几何体在Three.js中有预先定义,比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00020002.jpg?sign=1739010708-8hAidSGeCK2xyFlTv7eyvXrWfjLUOKmp-0-1588938f1b61c47ef3ca0f94d7d79d87)
6.材质
材(Material)用于反映一个3D对象的外观表现,如纹理图片、透明度、颜色、反射度等。比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00020003.jpg?sign=1739010708-MlipPgjEE9NVLoEGUF47y3xS1jfQ4eaa-0-f6d1565ce0ed826a7d2612f439f3a252)
2.1.5 动画原理
Three.js使用requestAnimationFrame关键函数来实现基本的动画功能,这是在HTML5中新引进的一个函数,类似于setTimeOut或setInterval函数,但两者有本质区别。setInterval函数以固定的时间间隔重绘制画板,如果渲染过于复杂,动画时间就会延长;但request AnimationFrame函数能够根据不同情况丢弃一些插值帧,以保证动画按时完成。下面的代码说明了动画工作的基本原理:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00021001.jpg?sign=1739010708-MZTrHDyavUoTOw7Jzqfo6Kdvi2Dxn6QW-0-afec0c2dca936559f413cfd9fe8ee95b)
代码运行的结果是一个立方体沿x轴不断旋转。requestAnimationFrame函数的重绘时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧,这个频率大大超过了电影动画的帧率(24帧/秒),因此可以获得更佳的动画效果。对于隐藏或不可见的元素,requestAnimationFrame将不会进行重绘,这就意味着更少的CPU、GPU和内存使用量。
因为requestAnimationFrame较为“年轻”,因而一些旧的浏览器使用的是试验期的名字:mozRequestAnimationFrame、webkitRequestAnimationFrame、msRequestAnimationFrame,为了支持这些浏览器,最好在调用之前先判断是否定义了requestAnimationFrame以及上述函数:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00021002.jpg?sign=1739010708-gfCSq6eLerm0TBzk1I7dknibudFJh4X8-0-034acfd876dfc0355705077ec1490267)
Window.setTimeout(callback,1000/60);的意思是,在最糟糕的情况下,浏览器会调用setTimeout函数完成动画的绘制,每秒60帧,即每帧约17ms。