![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=1739008962-gq9BjvQRVg2m3anxYxUIOVufgZVycp0K-0-4d63dcfb68fbac45bc063ef1ed2e98fe)
图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=1739008962-5cxsbQ6Eguae7qInQyHiKZ9ciVPVFDe3-0-c5970b69727313ce3a2ee810e8d9756c)
其中,fov代表摄像机的视野广度;w/h代表摄像机的宽高比,实践中一般设置为画布本身的宽高比;near代表近视点,小于这个距离的对象不能显示;far代表远视点,大于这个距离的对象不能显示。通过改变摄像机的位置、朝向和角度,即可改变画布中的内容,比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019002.jpg?sign=1739008962-04LHsaMQrSiuSdUSrOgThLBzVZSYJriE-0-7315acb7677d0db05b71adae677fa56b)
这两行代码的意思是将摄像机置于画布正外侧5 m的位置上,朝向原点。
2.1.3 正交投影摄像机
使用透视摄像机获得的结果类似于人眼在真实世界中所看到的效果(近大远小),如图2-2所示;而使用正交投影摄像机获得的结果则像在几何课上所画的效果,对于在三维空间内平行的线,投影到二维空间中也一定是平行的,如图2-3所示。一般说来,对于制图、建模软件通常使用正交投影,这样不会因为投影而改变物体比例;而对于其他大多数应用通常使用透视投影,因为这更接近人眼的观察效果。当然,摄像机的选择并没有对错之分,可以根据应用的特性选择一个效果更佳的摄像机。
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019003.jpg?sign=1739008962-88qFBwnlac1CTFjgWB8zBPGybVsXq5uH-0-3cace9ec2ddb85c721d207ebf23e9097)
图2-2 透视摄像机
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019004.jpg?sign=1739008962-CHCuPvGR8cV3wT5ZAVLQn662nPHVQZOE-0-04372a57b014bc15736d7dbce114e61d)
图2-3 三维空间内平行的线
正交投影摄像机(Orthographic Camera)设置起来较为直观,它的构造函数是:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019005.jpg?sign=1739008962-45VFtlnKNhr1gsWZgTDXFR4DXvKjVJBP-0-633bce2b6271593ff710a6312c0b904d)
这6个参数分别代表正交投影摄像机拍摄到的空间的6个面的位置,这6个面围成一个长方体,我们称其为视景体(Frustum),如图2-4所示。只有在视景体内部的物体才可能显示在屏幕上,而视景体外的物体会在显示之前被裁剪掉。
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00019006.jpg?sign=1739008962-9nOucdouAPTnittFu0sQ7BVZmB5kYNKf-0-c94ab641a8947e7c693e2fbdaa59bd45)
图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=1739008962-JTAIPMafO8RMhPbHWXkqHbtlKYRomGyA-0-89353375400cb8a664047f4d24621dab)
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=1739008962-ql1701Q4dG9WOfpqQRw7VpDh0C343axy-0-18b2a6513fbb6e1956ccccce034838ba)
6.材质
材(Material)用于反映一个3D对象的外观表现,如纹理图片、透明度、颜色、反射度等。比如:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00020003.jpg?sign=1739008962-uARPsvGXLdfHKYiL27xmeaDdRn3D68US-0-a32b4bd98d16c8b36ac99b95c3b0dc36)
2.1.5 动画原理
Three.js使用requestAnimationFrame关键函数来实现基本的动画功能,这是在HTML5中新引进的一个函数,类似于setTimeOut或setInterval函数,但两者有本质区别。setInterval函数以固定的时间间隔重绘制画板,如果渲染过于复杂,动画时间就会延长;但request AnimationFrame函数能够根据不同情况丢弃一些插值帧,以保证动画按时完成。下面的代码说明了动画工作的基本原理:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00021001.jpg?sign=1739008962-boXEOJXw9I27pFBMsTRSJNcPPyOi0XfM-0-966709edcfa50456f6daae04d684c38e)
代码运行的结果是一个立方体沿x轴不断旋转。requestAnimationFrame函数的重绘时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧,这个频率大大超过了电影动画的帧率(24帧/秒),因此可以获得更佳的动画效果。对于隐藏或不可见的元素,requestAnimationFrame将不会进行重绘,这就意味着更少的CPU、GPU和内存使用量。
因为requestAnimationFrame较为“年轻”,因而一些旧的浏览器使用的是试验期的名字:mozRequestAnimationFrame、webkitRequestAnimationFrame、msRequestAnimationFrame,为了支持这些浏览器,最好在调用之前先判断是否定义了requestAnimationFrame以及上述函数:
![](https://epubservercos.yuewen.com/9C24F9/17180253304506706/epubprivate/OEBPS/Images/img00021002.jpg?sign=1739008962-b2N7uYd6VprTi4ZSqpa3X1AMc5HmCC7M-0-a6363896bab33b995202e7af3013d99f)
Window.setTimeout(callback,1000/60);的意思是,在最糟糕的情况下,浏览器会调用setTimeout函数完成动画的绘制,每秒60帧,即每帧约17ms。