WebGL与three.js


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框架中的佼佼者,由于它的易用性和扩展性,使得它能够满足大部分的开发需求

  1. Three.js掩盖了3D渲染的细节:Three.js将WebGL原生API的细节抽象化,将3D场景拆解为网格、材质和光源(即它内置了图形编程常用的一些对象种类)。
  2. 面向对象:开发者可以使用上层的JavaScript对象,而不是仅仅调用JavaScript函数。
  3. 功能非常丰富:Three.js除了封装了WebGL原始API之外,Three.js还包含了许多实用的内置对象,可以方便地应用于游戏开发、动画制作、幻灯片制作、髙分辨率模型和一些特殊的视觉效果制作。
  4. 速度很快:Three.js采用了3D图形最佳实践来保证在不失可用性的前提下,保持极高的性能。
  5. 支持交互:WebGL本身并不提供拾取(picking)功能(即是否知道鼠标正处于某个物体上)。而Three.js则固化了拾取支持,这就使得你可以轻松为你的应用添加交互功能。
  6. 包含数学库:Three.js拥有一个强大易用的数学库,你可以在其中进行矩阵、投影和矢量运算。
  7. 内置文件格式支持:你可以使用流行的3D建模软件导出文本格式的文件,然后使用Three.js加载;也可以使用Three.js自己的JSON格式或二进制格式。
  8. 扩展性很强:为Three.js添加新的特性或进行自定义优化是很容易的事情。如果你需要某个特殊的数据结构,那么只需要封装到Three.js即可。
  9. 支持HTML5 canvas:Three.js不但支持WebGL,而且还支持使用Canvas2D、Css3D和SVG进行渲染。在未兼容WebGL的环境中可以回退到其它的解决方案。

ThreeJS缺点

虽然Three.js的优势很大,但是它也有它的不足之处:

  1. 官网文档非常粗糙,对于新手极度不友好。
  2. 国内的相关资源匮乏。
  3. Three.js所有的资料都是以英文格式存在,对国内的朋友来说又提高了门槛。
  4. Three.js不是游戏引擎,一些游戏相关的功能没有封装在里面,如果需要相关的功能需要进行二次开发。

其他WebGL库

  1. Babylon:它是最好的JavaScript3D游戏引擎,它能创建专业级三维游戏。主要以游戏开发和易用性为主。对于WebGL的封装,双方做的各有千秋,Three.js浅一些,好处是易于扩展,易于向更底层学习;Babylon.js深一些,好处是易用扩展难度大一些。
  2. PlayCanvas:是一个基于WebGL游戏引擎的企业级开源JavaScript框架,它有许多的开发工具能帮你快速创建3D游戏。但扩展性不如Three.js。
  3. Cesium:是国外一个基于JavaScript编写的使用WebGL的地图引擎,支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区域。Cesium是一个地图引擎,专注于Gis

核心的API

threeJS中有几个重要的概念,也是构建一个3D场景图必要的对象,分别如下

  1. 场景(Scene):是一个三维空间,所有物品的容器,可以把场景想象成一个空房间
  2. 相机(Camera):场景中的相机,代替人眼去观察,相机看到的内容就是最终屏幕上的效果
  3. 网格(Mesh):包括二维物体(点、线、面)、三维物体,模型等等
  4. 光源(Light):场景中的光照,如果不添加光照场景将会是一片漆黑,包括环境光、平行光、点光源等
  5. 渲染器(Renderer):场景的渲染方式,如webGL\canvas2D\Css3D。
  6. 控制器(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作为唯一的光源,而是和SpotLightDirectionalLight等光源结合使用,从而达到柔化阴影、添加全局色调的效果。 指定颜色时要相对保守,例如#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();

});

Author: 顺坚
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 顺坚 !
评论
 Previous
ajax和axios、fetch的区别 ajax和axios、fetch的区别
axios、fetch 和 ajax的区别 在网络上存在很多文章。现针对自己的情况,来重新整理一份,便于自己记忆和理解。内容参考了网络上的众多文章。 XMLHttpRequest浏览器通过XMLHttpRequest 对象进行 HTTP 通
2021-11-06
Next 
Antd中使用React-dnd Antd中使用React-dnd
这是一篇踩坑的记录,最近项目有个需求要用到Antd中的拖拽表格的拖拽效果用于表格数据的重排序,Antd的官方文档中也有拖拽表格的Demo例子。所以当SA问能否实现时,我微微的点了点头。由于种种原因我没有升级Antd和React版本,所以本文
2021-10-18
  TOC