WebGL (全写Web Graphics Library) 是一种3D绘图协议,并提供了JavaScript API,,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0(OpenGL for Embedded Systems,OpenGL嵌入式版本,针对手机、游戏机等设备相对较轻量级的版本)结合在一起,用于在任何兼容的Web浏览器中呈现交互式3D和2D图形,而无需使用插件。WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。
ThreeJS
Three.js是WebGL的JavaScript 3D库,其对WebGL提供的接口进行了非常好的封装,它是由居住在西班牙巴塞罗那的程序员Ricardo Cabbello Miguel开发的,此人更出名的网名叫做Mr.doob。Three.js以简单、直观的方式封装了3D图形编程中常用的对象。Three.js在开发中使用了很多图形引擎的高级技巧,极大地提高了性能。另外,由于内置了很多常用对象和极易上手的工具,Three.js的功能也非常强大。最后,Three.js还是完全开源的,你可以在GitHub上找到它的源代码,并且有很多人贡献代码,帮助Mr.doob一起维护这个框架。Three.js简化了很多WebGL API的细节,大大降低了学习成本,成为前端开发者完成3D绘图的得力工具。
WebGL和Three.js的关系
WebGL原生的api是一种非常低级的接口,而且还需要一些数学和图形学的相关技术。对于没有相关基础的人来说,入门真的很难,Three.js将入门的门槛降低了整整的一大截,对WebGL进行封装,简化我们创建三维动画场景的过程。用最简单的一句话概括:WebGL和Three.js的关系,相当于JavaScript和Jquery的关系。
three.js官方文档 :threejs.org/
three.js中文文档 : techbrood.com/threejs/doc…
推荐文档地址:http://www.webgl3d.cn/
ThreeJS优点
Three.js作为WebGL框架中的佼佼者,由于它的易用性和扩展性,使得它能够满足大部分的开发需求
- Three.js掩盖了3D渲染的细节:Three.js将WebGL原生API的细节抽象化,将3D场景拆解为网格、材质和光源(即它内置了图形编程常用的一些对象种类)。
- 面向对象:开发者可以使用上层的JavaScript对象,而不是仅仅调用JavaScript函数。
- 功能非常丰富:Three.js除了封装了WebGL原始API之外,Three.js还包含了许多实用的内置对象,可以方便地应用于游戏开发、动画制作、幻灯片制作、髙分辨率模型和一些特殊的视觉效果制作。
- 速度很快:Three.js采用了3D图形最佳实践来保证在不失可用性的前提下,保持极高的性能。
- 支持交互:WebGL本身并不提供拾取(picking)功能(即是否知道鼠标正处于某个物体上)。而Three.js则固化了拾取支持,这就使得你可以轻松为你的应用添加交互功能。
- 包含数学库:Three.js拥有一个强大易用的数学库,你可以在其中进行矩阵、投影和矢量运算。
- 内置文件格式支持:你可以使用流行的3D建模软件导出文本格式的文件,然后使用Three.js加载;也可以使用Three.js自己的JSON格式或二进制格式。
- 扩展性很强:为Three.js添加新的特性或进行自定义优化是很容易的事情。如果你需要某个特殊的数据结构,那么只需要封装到Three.js即可。
- 支持HTML5 canvas:Three.js不但支持WebGL,而且还支持使用Canvas2D、Css3D和SVG进行渲染。在未兼容WebGL的环境中可以回退到其它的解决方案。
ThreeJS缺点
虽然Three.js的优势很大,但是它也有它的不足之处:
- 官网文档非常粗糙,对于新手极度不友好。
- 国内的相关资源匮乏。
- Three.js所有的资料都是以英文格式存在,对国内的朋友来说又提高了门槛。
- Three.js不是游戏引擎,一些游戏相关的功能没有封装在里面,如果需要相关的功能需要进行二次开发。
其他WebGL库
- Babylon:它是最好的JavaScript3D游戏引擎,它能创建专业级三维游戏。主要以游戏开发和易用性为主。对于WebGL的封装,双方做的各有千秋,Three.js浅一些,好处是易于扩展,易于向更底层学习;Babylon.js深一些,好处是易用扩展难度大一些。
- PlayCanvas:是一个基于WebGL游戏引擎的企业级开源JavaScript框架,它有许多的开发工具能帮你快速创建3D游戏。但扩展性不如Three.js。
- Cesium:是国外一个基于JavaScript编写的使用WebGL的地图引擎,支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区域。Cesium是一个地图引擎,专注于Gis
核心的API
threeJS中有几个重要的概念,也是构建一个3D场景图必要的对象,分别如下
- 场景(Scene):是一个三维空间,所有物品的容器,可以把场景想象成一个空房间
- 相机(Camera):场景中的相机,代替人眼去观察,相机看到的内容就是最终屏幕上的效果
- 网格(Mesh):包括二维物体(点、线、面)、三维物体,模型等等
- 光源(Light):场景中的光照,如果不添加光照场景将会是一片漆黑,包括环境光、平行光、点光源等
- 渲染器(Renderer):场景的渲染方式,如webGL\canvas2D\Css3D。
- 控制器(Control): 可通过键盘、鼠标控制相机的移动
下面我们依次详细学习以上的细分知识点。
相机
ThreeJS中我们常用的有两种类型的相机:正交(orthographic)相机
、透视(perspective)相机
。一般情况下为了模拟人眼我们都是使用透视相机; 正交镜头的特点是,物品的渲染尺寸与它距离镜头的远近无关。也就是说在场景中移动一个物体,其大小不会变化。正交镜头适合2D游戏。透视镜头则是模拟人眼的视觉特点,距离远的物体显得更小。透视镜头通常更适合3D渲染。但是理解照相机的情况下,先理解另一个概念——视椎体
视锥体是摄像机可见的空间,看上去像截掉顶部的金字塔。视锥体由6个裁剪面围成,构成视锥体的4个侧面称为上左下右面,分别对应屏幕的四个边界。为了防止物体离摄像机过近,设置近切面(Near plane),同时为了防止物体离摄像机太远而不可见,设置远切面(Far plane)。
创建一个透视相机:let camera = new THREE.PerspectiveCamera(fov,aspect,near,far)
参数 | 解释 |
---|---|
fov | 视野角度,从镜头可以看到的场景的部分。通常3D游戏的FOV取值在60-90度之间较好的默认值为60 |
aspect | 渲染区域的纵横比。较好的默认值为window.innerWidth/window.innerHeight |
near | 摄像机视锥体近端面 |
far | 摄像机视锥体远端面 |
创建摄像机以后还要对其进行移动、然后对准物体积聚的场景中心位置,分别是设置其 position和调用 lookAt 方法,参数均是一个 xyz向量(new THREE.Vector3(x,y,z))
camera.position:控制相机在整个3D环境中的位置(取值为3维坐标对象-THREE.Vector3(x,y,z))
camera.lookAt:控制相机的焦点位置,决定相机的朝向(取值为3维坐标对象-THREE.Vector3(x,y,z))
光源
在ThreeJs中光源是必须的,如果一个场景你不设置灯光那么世界将会是一片漆黑。ThreeJs内置了多种光源以满足特定场景的需要。可以根据自己的项目需要来选择何种灯光
参数 | 解释 |
---|---|
AmbientLight | 环境光,其颜色均匀的应用到场景及其所有对象上。 这种光源为场景添加全局的环境光,这种光没有特定的方向,不会产生阴影。 通常不会把AmbientLight 作为唯一的光源,而是和SpotLight 、DirectionalLigh t等光源结合使用,从而达到柔化阴影、添加全局色调的效果。 指定颜色时要相对保守,例如#0c0c0c。设置太亮的颜色会导致整个画面过度饱和,什么都看不清。 |
PointLight | 3D空间中的一个点光源,向所有方向发出光线 |
SpotLight | 也就无限光,光线是平行的。典型的例子是日光,用于模拟遥远的,类似太阳那样的光源。 该光源与 SpotLight 的主要区别是,它不会随着距离而变暗,所有被照耀的地方获得相同的光照强度。 |
HemisphereLight | 特殊光源,用于创建户外自然的光线效果。 此光源模拟物体表面反光效果、微弱发光的天空,模拟穹顶(半球)的微弱发光效果,让户外场景更加逼真。 使用DirectionalLight + AmbientLight 可以在某种程度上来模拟户外光线,但是不够真实,因为无法体现大气层的散射效果、地面或物体的反射效果 |
AreaLight | 面光源,指定一个发光的区域 |
LensFlare | 不是光源,用于给光源添加镜头光晕效果 |
关于光源的详细 API 可以参考 threejs 官网,很详细,demo 也很完整 传送门
网格
在计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。当线段数量越多,长度就越短,当达到你无法察觉这是线段时,一条平滑的弧线就出现了。 计算机的三维模型也是类似的。只不过线段变成了平面,普遍用三角形组成的网格来描述。我们把这种模型称之为 Mesh 模型。
在 threeJs 的世界中,材质(Material)+几何体(Geometry)就是一个 mesh。设置其name属性可以通过
scene.getObjectByName(name)
获取该物体对象;Geometry就好像是骨架,材质则类似于皮肤,对于材质和几何体的分类见下表格
材质分类
材质 | 说明 |
---|---|
MeshBasicMaterial | 基本的材质,显示为简单的颜色或者显示为线框。不考虑光线的影响 |
MeshDepthMaterial | 使用简单的颜色,但是颜色深度和距离相机的远近有关 |
MeshNormalMaterial | 基于面Geometry的法线(normals)数组来给面着色 |
MeshFacematerial | 容器,允许为Geometry的每一个面指定一个材质 |
MeshLambertMaterial | 考虑光线的影响,哑光材质 |
MeshPhongMaterial | 考虑光线的影响,光泽材质 |
ShaderMaterial | 允许使用自己的着色器来控制顶点如何被放置、像素如何被着色 |
LineBasicMaterial | 用于THREE.Line对象,创建彩色线条 |
LineDashMaterial | 用于THREE.Line对象,创建虚线条 |
RawShaderMaterial | 仅和THREE.BufferedGeometry联用,优化静态Geometry(顶点、面不变)的渲染 |
SpriteCanvasMaterial | 在针对单独的点进行渲染时用到 |
SpriteMaterial | 在针对单独的点进行渲染时用到 |
PointCloudMaterial | 在针对单独的点进行渲染时用到 |
2D图形
图形 | 说明 |
---|---|
THREE.PlaneGeometry | 外观上是一个矩形new THREE.PlaneGeometry(width, height, widthSegments, heightSegments) |
THREE.CircleGeometry | 外观上是一个圆形或者扇形 半径为3的圆 new THREE.CircleGeometry(3, 12) ; 半径为3的半圆new THREE.CircleGeometry(3, 12, 0, Math.PI) ;第三个参数和第四个分别是起始角度和结束角度,默认0-2*PI |
THREE.RingGeometry | 外观上是一个圆环或者扇环new THREE.RingGeometry(innerRadius, outerRadius, thetaSegments, phiSegments,thetaStart, thetaLength) |
THREE.ShapeGeometry | 该形状允许你创建自定义的二维图形,其操作方式类似于SVG/Canvas中的画布 |
3D图形
图形 | 说明 |
---|---|
THREE.BoxGeometry | 这是一个具有长宽高的盒子BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments) |
THREE.SphereGeometry | 这是一个三维球体/不完整球体SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) |
THREE. CylinderGeometry | 可以绘制圆柱、圆筒、圆锥或者截锥 |
加载外部模型
一般来讲我们的场景中可能是一些奇奇怪怪的形状,或多或少项目中都会用到一些外部的模型资源,比如动物啊,装饰物啊什么的,再加上一些动画,这样整个场景更加显得生动,那么 threejs 中我们可以通过哪些方式来加载外部的模型资源呢?加载外部模型,是通过Three.js加载器(Loader)实现的。加载器把文本/二进制的模型文件转化为Three.js对象结构。 每个加载器理解某种特定的文件格式。需要注意的是,由于贴图的尺寸必须是(2的幂数)X (2的幂数),如:1024X512,所以为了防止贴图变形,平面的宽度比例需要与贴图的比例一致。加载外部模型数据支持以下格式
格式 | 说明 |
---|---|
JSON | Three.js自定义的、基于JSON的格式。 可以声明式的定义一个Geometry或者Scene.利用该格式,你可以方便的重用复杂的Geometry或Scene |
OBJ / MTL | OBJ是Wavefront开发的一种简单3D格式,此格式被广泛的支持,用于定义Geometry,MTL用于配合OBJ,它指定OBJ使用的材质 |
Collada(dae) | 基于XML的格式,被大量3D应用程序、渲染引擎支持 |
STL | STereoLithography的简写,在快速原型领域被广泛使用。 3D打印模型通常使用该格式定义Three.js提供了STLExporter.js,使用它可以把Three.js模型导出为STL格式 |
CTM | openCTM定义的格式,以紧凑的格式存储基于三角形的Mesh |
VTK | Visualization Toolkit定义的格式,用于声明顶点和面。 此格式有二进制/ASCII两种变体,Three.js仅支持ASCII变体 |
AWD | 3D场景的二进制格式,主要被away3d引擎使用,Three.js不支持AWD压缩格式 |
Assimp | 开放资产导入库(Open asset import library)是导入多种3D模型的标准方式。 使用该Loader你可以导入多种多样的3D模型格式 |
VRML | 虚拟现实建模语言(Virtual Reality Modeling Language)是一种基于文本的格式。 现已经被X3D格式取代尽管Three.js不直接支持X3D,但是后者很容易被转换为其它格式 |
Babylon | 游戏引擎Babylon的私有格式 |
PLY | 常用于存储来自3D扫描仪的信息 |
在项目一开始尝试是使用 dae 文件,后面发现 json 文件更加方便一点,所以最终使用的是 jsonloader 导入 json 文件。json文件可以通过 blender 或者3DsMax 导出,他们都有各自的 export json的插件。在软件中处理好模型贴图和动画以后,导出 json 文件和相应的贴图文件给到前端即可。
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load('model.json', function (geometry, materials) {
materials.forEach(function (mat) {
//这里面可以设置材质的各种信息
mat.skinning = true;
mat.color = new THREE.Color("rgb(233,203,113)"); //模型颜色
mat.emissive = new THREE.Color("rgb(110,110,110)"); //自发光颜色
});
var model = new THREE.SkinnedMesh(geometry, new THREE.MeshFaceMaterial(materials));
model.name = "model name";
scene.add(model); //下面是播放模型中的动画内容
var sceneAnimationClip = model.geometry.animations[0]
var mixer = new THREE.AnimationMixer(model);
mixers.push(mixer);
var sceneAnimation = mixer.clipAction(sceneAnimationClip);
sceneAnimation.play();
});