Sugar 是我们从零开始开发的 BI 产品,可以不用写 SQL 制作报表及大屏页面,上半年我们发布了三维场景功能,可以放到大屏中展现:
为了实现这个功能,我们调研了大量 WebGL 相关框架和库,整理了这篇文章,或许以后对你有帮助,赶紧先收藏吧。
引擎类型
WebGL 框架和引擎按照定位可以分成这三种类型:
- WebGL 封装,定位是简化 WebGL 开发,最大的特点是必须自己写 GLSL 才能用。
- 渲染引擎,定位是三维物体及场景展示,一般会抽象出场景、相机、灯光等概念,上手门槛低,不需要自己写 GLSL。
- 游戏引擎,定位是游戏开发,在前面的渲染引擎基础上,还提供了骨骼动画、物理引擎、AI、GUI 等功能,以及可视化编辑器来设计关卡,支撑大型游戏的开发。
WebGL 封装
先说 WebGL 封装,这种库主要解决的问题是 WebGL 的 API 过于繁琐。
WebGL 源自 OpenGL,它最早可以追溯到 1992 年,那个时候还是以 C 这种面向过程式的语言为主,所以 OpenGL 的 API 也是过程式的,对于熟悉面向对象的开发者来说,它的代码看起来冗长且可读性差,因此有必要对其进行封装和简化。
twgl.js
twgl.jsgithub.comtwgl.js 就是最典型的做法,比如创建一个最常见物体在 WebGL 中需要这样写,其中反复调用 bindBuffer 和 bufferData,很容易写错:
const positions = [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1];
const normals = [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1];
const texcoords = [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1];
const indices = [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23];const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
const indicesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
而使用 twgl.js 就能简化成这样:
const arrays = {position: [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1],normal: [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1],texcoord: [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1],indices: [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl 的定位只是减少重复代码,并没有进一步抽象,所以使用它和直接用 WebGL 在学习成本上没太大区别,因此非常适合初学者,但也意味着它没什么独特的功能。
regl
reglgithub.com和 twgl 单纯简化代码相比,regl 提供了跟高层的抽象,将原本的过程式转成了函数式,使得看起来更符合直觉,比如下面这个入门三角形比原生 WebGL 要少很多代码。
const drawTriangle = regl({frag: `void main() {gl_FragColor = vec4(1, 0, 0, 1);}`,vert: `attribute vec2 position;void main() {gl_Position = vec4(position, 0, 1);}`,attributes: {position: [[0, -1], [-1, 0], [1, 1]]},count: 3
})
regl 的原理是动态生成 WebGL 相关的 JavaScript 代码然后执行,所以它比 twgl 能提供更加简化的代码,也能更灵活地设计对外 API,减少 WebGL 本身过程式带来的限制,功能也更多,比如能自动处理状态丢失。
从工程角度看 regl 做得很不错,文档详尽,有 30000 单元测试,覆盖率达到了 95%,还有工具来追踪性能变化,可以很放心地使用。
但由于做了一层封装,导致使用它和原生 WebGL 写法差异较大,因此不适合对 WebGL 还不熟悉的初学者,但对于熟悉 WebGL 的开发者来说使用它的开发体验不错。
OGL
oframe/oglgithub.comOGL 的定位比较特别,它有点介于 WebGL 封装和渲染引擎之间,比如它最简单的这个例子:
import {Renderer, Camera, Transform, Box, Program, Mesh } from 'ogl';
const renderer = new Renderer();
const gl = renderer.gl;
document.body.appendChild(gl.canvas);
const camera = new Camera(gl);
camera.position.z = 5;
const scene = new Transform();
const geometry = new Box(gl);const program = new Program(gl, {vertex: `attribute vec3 position;uniform mat4 modelViewMatrix;uniform mat4 projectionMatrix;void main() {gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragment: `void main() {gl_FragColor = vec4(1.0);}`,
});const mesh = new Mesh(gl, {geometry, program});
mesh.setParent(scene);
renderer.render({scene, camera});
可以看到它一方面提供了渲染引擎才有的场景树、相机等概念,另一方面又没有材质和光源,所以需要自己写 Shader 来完成基本渲染。
因此我觉得它比较适合 TA(Technical Artist) 使用,基于它开发可以不用了解太多 WebGL 的细节,专注于图形学算法,而且它的 Uniform 变量名和 Three.js 是一样的,网上找基于 Three.js 写的例子都能直接用。
不过如果当成 WebGL 封装,它成熟度不如 regl,因为缺乏单元测试使得预计会有不少 bug,而如果当成渲染库在功能上又没法和 Three.js 比,因此主要优势似乎只有体积小了。
luma.gl
luma.glluma.glluma.gl 是 Uber 开发的,主要用它开发地理可视化框架,比如 Desk.gl 和 Kepler.gl,还有无人车数据可视化 AVS。
使用前面几个库要同时支持 WebGL 2.0 和 1.0 需要自己做兼容,而 luma.gl 可以自动解决这个问题,方便在支持 WebGL 2.0 的设备上优先使用 WebGL 2.0,比如直接调用 createVertexArray,不过这种 api 没几个,所以这个亮点倒是不显著。
它的独特功能其实是 Shader 模块化拆分,这对于写复杂的 Shader 很有帮助。
渲染引擎
由于 WebGL 本身只是光栅引擎,基于它开发需要了解矩阵变换并编写着色器,所以 WebGL 学习门槛很高,光入门就要看特别长的文档,比如 WebGL Fundamentals 这个教程系列就有 60 多篇文章。
WebGL Fundamentalswebglfundamentals.org相比之下使用渲染引擎就容易得多,它将其中的矩阵变化封装成了相机、场景树,并提供了材质和光源,运行时自动生成对应的 GLSL,使得即使完全不懂 WebGL 也能用,大大降低了门槛。
对于大部分应用而言,比起前面的 WebGL 封装,最好还是选择渲染引擎,因为大部分渲染引擎也提供了自定义 Shader 功能,也提供了 GPU 实例等功能,只是一般不能改渲染管线。
Filament
google/filamentgithub.comFilament 是 Google 基于 C++ 开发的跨平台物理渲染引擎,支持 Android、iOS、Windows、Mac 等系统,还提供了基于 WebAssembly 的 Web 版本,它用在了 Google 地图和搜索这两个核心 APP 中,因此大概率会长期维护,不用担心弃坑。
值得一提的是 Filament 的文档写得极好,相当于一本如何实现 PBR 的教材,可以学到很多图形学的知识。
Physically Based Rendering in Filamentgoogle.github.io尽管只是顺带支持 Web,但完善的实现使得它的渲染效果很突出,在渲染管线上使用了较为新颖的 Clustered forward renderer,因此能支持大量光源。
它生成的 wasm 文件有 2M,不算太大,我原本打算将它引入到 Sugar 中,但这个渲染库在 Web 上使用有两个缺点:
- 核心是 C++ 写的,如果功能不满足只能去改 C++,而这几乎是必须的,因为暴露给 JavaScript 的 API 很少,最简单的点击交互都不支持,各个组件的设置也很少,比如相机要实现无交互时自动旋转就得改 C++ 代码,不熟悉 C++ 将寸步难行。
- 只支持 WebGL 2.0,这点比较致命,导致无法在 iOS 的浏览器上使用,看了一下它有调用 glDrawBuffers,但不清楚用于做什么。
整体上我觉得它是学习 PBR 渲染实现原理的优秀项目,虽然暂时不适合在 Web 上用,但值得继续关注。
Claygl
ClayGLclaygl.xyzClaygl 是 ECharts 核心开发者 pissang 大神的开发的 WebGL 游戏引擎,它还用在了 ECharts-gl 项目中,使得 ECharts 在三维图表方面远超所有竞品,这点我在《如何挑选数据可视化框架及平台 - 前端篇》里介绍过。
在我看来它最大的亮点是支持延迟着色,目前除了前面提到的 Filament 和后面的 LayaAir,其它 WebGL 引擎都是传统的前向着色,这种管线在渲染时,会对每个物体计算所有光照的贡献,类似如下的写法:
for node of nodes:for light of lights:output += bdrf(node, light)
这种实现的复杂度随着光源和场景中对象的增加而增加,导致光源越多性能越差,这也是为什么在 Web 上看到的三维效果大部分都只使用一个主光源,类似下面这种效果,只有一个太阳光:
如果想实现接近现实世界的效果就必须支持多光源,尤其是室内和夜晚,比如类似下面这种,使用前向渲染性能太差,无法做到实时渲染。
要解决这个问题只能使用延迟着色或者分簇(Clustered)前向着色,目前主流的桌面游戏引擎都是使用延迟着色,并配合前向着色来支持透明物体,相关细节推荐看看 lygyue:延迟渲染,ClayGL 也是目前唯一实现这种管线的开源渲染引擎。
不过在 WebGL 中实现延迟着色最大的问题是兼容性,因为它依赖 MTR(Multiple Render Targets) 技术,只有在 WebGL 2.0 下原生支持,在 WebGL 1.0 中必须依赖「WEBGL_draw_buffers」扩展,但它的兼容性较差,目前桌面浏览器的支持率也只有 71%,在手机上更是只有 2%,想在手机上使用只能等 WebGL 2.0 普及,但 iOS 一直默认不开启,不知道要再等几年了。
Litescene.js
https://github.com/jagenjo/litescene.jsgithub.comLitescene.js 主要用于开发 WebGL 场景编辑器 WebGLStudio,它其中有些 API 就是专门给编辑器用的,WebGLStudio 是少有的开源 WebGL 编辑器,功能很丰富,但使用体验不好,给我的感受是必须用过 Unity 等编辑器才会用,上手门槛有点高。
这个项目的作者也是不容易,几乎一个人开发了从 WebGL 到前端的所有功能,还开发了个前端 UI 库 litegui,不过技术栈比较古老,一个人精力还是有限,代码中有不少地方都没空整理,比如加了个新文件但老的还没空删,而且也没有 release 版本,所以不建议使用。
Hilo3d
https://github.com/hiloteam/Hilo3dgithub.comHilo3d 是来自支付宝的项目,在 github 上最早提交时间是 2019 年 8 月,所以是这里面出现最晚的渲染引擎,在它之上还有个游戏引擎 Sein,它支持使用 Unity 作为场景编辑器,和 LayaAir 类似,但这个即是优点也是缺点,虽然省去了编辑器的开发,但导出效果很可能不一致,要反复调整。
这个渲染引擎最初目的是用于支付宝里自己研发的小游戏,所以重点是支持移动端,但这也将会是它的限制,比如追求体积小,图形方面更重视性能而不是视觉效果,以及后期特效比较少等。
从提交看目前基本只有一个人,但看起来并不在 github 上开发,更像是拿 github 来定期发布版本。
osgjs
https://github.com/cedricpinson/osgjsgithub.comogsl 借鉴了 OpenSceneGraph 的 API,很适合拿来构建三维场景,但这个项目已经停止了,主要原因是核心人员跑去创业了,它的核心开发人员之一成为了著名在线三维模型网站 Sketchfab 的 CTO,Sketchfab 的模型渲染器就是在它基础上开发的,加上了后期特效等功能,算是 Web 领域效果最好的渲染器了。
xeogl
xeolabs/xeoglgithub.comxeogl 很适合用来展示建筑及工业模型,它提供了标注、公告板以及相机动画等功能,这些功能在其他引擎中都得自己实现。
不过这个项目作者不打算继续维护了,估计是开源项目收益太小,作者目前主要在开发 xeokit,这个两个项目的定位是一样的,但 xeokit 商用要收费,一次性收 €2999,它对建筑类的项目很友好,内置了许多 BIM 相关的定制功能,比如对 ifc 格式和 BIMServer 的支持,还有支持截面浏览,所以使用它可以节省大量开发成本。
不过它在渲染效果方面的功能不多,后期特效只有一个 SAO,因此更适合朴实无华的工程项目展示。
A-Frame
aframevr/aframegithub.comA-Frame 是专注做 VR 的渲染库,最早是 Mozilla 开发的,目前主要是 Supermedium 的两个工程师和一个谷歌的工程师兼职开发,它的底层渲染基于 Threejs,提供了 inspector 功能,能很方便测试效果。
不过我每次玩 VR 都晕到吐,所以再也不关注这个领域了,晕问题是人脑的机制,只能靠几万年的进化来解决,而且还得是不晕的人比晕的人有生存优势,所以我是等不到 VR 会火的那天了。
游戏引擎
游戏引擎在渲染器的基础上增加了面向游戏开发的各种功能,包括 AI、物理、编辑器等,工作量巨大,比起图形学算法,更重要还有工程能力,完整的游戏引擎功能可以参考《Game Engine Architechture》这本书,下面的架构图就是来自这本书,可以看到它所覆盖的面相当广。
Unreal Engine
目前最火的游戏引擎是 Unreal Engine 和 Unity,它们都可以使用 Emscripten 编译出 WebAssembly 版本的项目,直接运行在浏览器中。
几年前在 WebGL 领域中最让人印象深刻的 demo 就是 Unreal 就和 Firefox 合作的这个项目,当时我也试过,在等了十几分钟加载几十 M 的 JS 文件后终于跑起来了,但非常卡。
知乎视频www.zhihu.com这个 demo 是 2014 年的,但渲染效果放到今天来看都很惊艳,秒杀绝大部分基于 Three.js/Babylon.js 开发的项目,甚至再过几年 Three.js/Babylon.js 也做不到,因为需要靠编辑器来优化间接光照。
前段时间我尝试编译过专门给移动端的 SunTemple 项目,在我的黑苹果 RX 5700 XT 上虽然不卡了,但体积太大,光引擎本身的 wasm 文件就有 82MB,数据文件也有 180MB,这样的体积是不可能放在 Web 上运行的,难怪没人用。
估计也是因为没什么人用,Unreal Engine 从 4.24 版本开始不默认提供这个功能,只作为扩展存在,交给社区,要用得自己编译一个,所以 Unreal Engine 目前已经基本放弃了 HTML5 版本。
Unity
相比之下 Unity 编译出来的体积小得多,自带的简单 3D 项目编译出 wasm 只有 4M,所以虽然也很少人用,但至少在线上有真实见到过几个。
值得一提的是 Unity 还在开发专门针对小游戏的 Project Tiny 版本,相当于一个精简版的 Unity,它输出的体积更小,比如这个官方的 Tiny Racing 项目 wasm 只有 607k,即便是所有模型和图片加起来的体积也只有 4.4M,虽然这个项目还在预览阶段,很多重要功能缺失,但 Unity 目前普及度高,所以它未来有不小潜力,但它在国内的发展取决于官方的是否重视,比如会不会支持微信等。
Godot
https://godotengine.org/godotengine.orgGodot 是目前最火的开源游戏引擎,它有 1182 个贡献者,提交很频繁,最近在开发的 4.0 版本,将支持 Vulkan API,并在渲染方面做了加强,比如支持 SDFGI。
它也能导出 WebGL 版本,但只是「能导出」,并没有专门优化过,拿几个材质测试了一下生成的 wasm 有 20M,但性能太差,在我的 i9 + RX 5700 XT 上都卡成 PPT,而且卡顿这个问题官方也不打算修了,预计 Godot 短期不会在 Web 领域有所发展,只能等它未来或许会支持 WebGPU 了。
Three.js
https://threejs.org/threejs.orgThree.js 是最知名的 WebGL 项目,Contributions 人数高达 1313,和 React 是一个量级的,尽管它自身的定位只是渲染引擎,但社区硬是把不少游戏引擎的功能都加上了,比如物理引擎、贴花、动画等,在源码中有大量例子,很适合学习,但不少重要功能,比如 gltf 加载器,都是放在 examples 目录里,让人感觉很不正式。
Three.js 的历史几乎和 WebGL 一样长,它早在 2010 年 7 月 7 日就支持 WebGL 渲染了,那个时候 WebGL 规范还在草案中,要等到 2011 年 3 月才正式发布,恐怕这就是为什么提到 WebGL 大家都会想到 Three.js,它大概是第一个支持 WebGL 的引擎。
由于知名度最高,Three.js 最大的优势就是社区强大,搜索问题能找到很多答案,也有非常多开源和商业项目使用,比如 Google 的 WebGL Globe、model-viewer、NASA 的 WorldWind、Autodesk 的 Forge Viewer 等。
但 Three.js 在版本管理方面很不专业,到现在都还没采用 semver 版本命名规范,每次发布都是一个叫 rXXX 的版本,我见过不少基于 Three.js 的项目都是固定在某个版本不敢升级了,比如 Autodesk 就提到过。
虽然 Three.js 有很多人使用,但因为整体代码质量一般,我只推荐用来学习,而不是用在正式项目中。
PlayCanvas
https://github.com/playcanvas/enginegithub.comPlayCanvas 虽然开源了游戏引擎,但编辑器只有在线服务,所以它的文档都是介绍如何使用在线编辑器来制作三维场景,并没有直接使用这个引擎的入门文档,要用只能通过 example 和 api 来了解,看起来官方并不希望大家直接使用引擎,所以如果不想用它的在线编辑器,这个引擎就只适合学习过其它引擎的开发者。
从引擎功能角度看弱于 Three.js 和 Babylon,但成熟度比 Three.js 好,Three.js 的很多功能是第三方贡献的,质量参差不齐,注释也很少。
虽然它没使用延迟着色,但提供了运行时 bake 的功能,也能高效支持静态多光源。
PlayCanvas 其实在 2017 年被 Snap 悄悄收购了,被期望于制作 Snap 上的 HTML5 游戏。
Egret
egret-labs/egret3dgithub.comEgret 和后面介绍的 LayaAir 和 cocos 都是国内创业公司开发的游戏引擎,Egret 最早是通过一款《围住神经猫》的 HTML5 游戏莫名其妙火的,它最早只支持 2D,但也在 2018 年 5 月推出了开源的 Egret 3D。
Egret 3D 引擎使用了 ECS 架构,所以它的编辑器提供了类似 Unity 那样添加组件的能力。
但 Egret 3D 开源后没多久就陷入停滞状态了,最新发布的版本是 2018 年 9 月,据说是在重构新版,然而已经过去一年了,可能 3D 并不是公司的重点,开源的版本甚至连 license 都没说明,加上文档比较简陋,所以不推荐使用。
LayaAir
https://github.com/layabox/LayaAirgithub.comLayaBox 公司最早推出的是 LayaFlash 工具来将 Flash 页游装成 HTML5 版本,随着 Flash 的没落,他们又开发了基于 Web 技术的 LayaAir 引擎。
LayaAir 的三维编辑器主要依赖 Unity,它甚至不能直接使用 WebGL 中最常用的 glTF 格式,要使用三维模型必须先导入到 Unity 中,然后再通过插件转成 LayaAir 所使用的格式。
比起 Three.js/Babylon,LayaAir 有两个比较大的优势,一个是对小程序支持友好,这个算是国内特色,Three.js/Babylon j的核心开发者也没条件测试,所以实际用起来容易遇到 bug,玩玩需要改引擎本身代码才能解决;另一个是近期实现了 Clustered Forward 渲染,可以支持大量光源。
LayaAir 还提供了生成原生 Android/iOS 程序的 LayaNative,这里并非使用 WebView,所以更容易过审。
但需要注意 LayaAir 只是源码开放,并不是真正的开源项目,使用前需要仔细阅读它的协议,比如未经授权是不允许对引擎代码进行修改的,免费使用需要加 LayaBox 的 logo。
从提交历史看,LayaAir 提交量最多的开发者在今年 4 月份忽然停止了,似乎是被阿里挖走了,不知道对引擎本身的发展会有多大影响。
cocos
Cocos引擎_游戏开发引擎www.cocos.comcocos 曾经是最流行的 2D 手游引擎,但随着游戏逐渐转向 3D,它在 3D 方面和 Unity 差距太大,就渐渐淡出大家的视野了。
cocos 所属的触控科技本来打算 2014 年在美国上市,但由于对估值不满意,尤其是 cocos2d-x 的 MIT 协议被认为价值几乎为零,所以最后放弃了上市,具体细节可以看看创始人的回答cocos2dx 还有未来么? - 陈昊芝的回答,其中还提到了和 Unity 的故事,比如本来还想收购 Unity 但被拒了,在放弃上市后,触控经历了很多危机,人数也收缩为之前的 1/5,从那时起 cocos2d-x 其实就在走下坡路了,逐渐被 Unity 超越。
尽管很艰难,触控一直没放弃引擎的开发,在 2019 年 10 月发布了 Cocos Creator 3D,和 cocos2d-x 基于 C++ 不同,Cocos Creator 3D 是基于 TypeScript 开发的 WebGL 引擎。
在协议方面,Cocos Creator 3D 吸取了 cocos2d-x 的教训,和 LayaAir 一样只是源码开放,它也有一份定制的协议,有很多限制,需要仔细阅读。
尽管 Cocos Creator 3D 很想成为 Unity,编辑器在很多细节点上都参考了 Unity,比如资源管理的 .meta 文件,基于 ECS 的组件机制等,但 WebGL 的限制使得它只能用做小游戏的引擎,因为 OpenGL ES 2.0 功能的缺失,虽然可以发布到微信、百度、支付宝等平台上,但在重度游戏领域没法和 Unity 竞争。
引擎方面功能和 LayaAir 类似,不过它有动画、例子编辑器,在编辑器方面比 LayaAir 好得多,不依赖 Unity,不过因为使用了前向着色,同样不能支持多光源,虽然它基于 AABB 包围盒做了光源裁剪,但这种方式的性能比较依赖场景光源和物体的分布情况,比如物体很大又有很多小光源的时候,几乎裁剪不掉几个光源。
Babylon
https://babylonjs.com/babylonjs.com最后压轴的是 Babylon,它也是 Sugar 最终采用的 WebGL 引擎,不仅功能强大,代码质量也很高,TypeScript 类型完善,几乎每个函数都有注释。
我个人的使用体会是 Babylon 虽然入门要复杂点,但功能成熟度要比 Three.js 高不少,Three.js 至今在 gLTF 的支持上还有 bug,而 Babylon 是唯一通过所有测试的框架,如果要深入使用 gLTF,Babylon 是最好选择,因为它还支持大量扩展,比如 KHR_mesh_quantization、KHR_draco_mesh_compression、KHR_texture_basisu、MSFT_lod 等,这些扩展能显著减小体积和提升性能。
Babylon 在材质方面功能丰富,除了基础的 PBR,还提供了用于皮肤的次表面渲染 SubSurface、用于车漆的 ClearCoat、用于布料的 Sheen,以及用于光盘之类的各向异性材质 Anisotropy 等等。
在后期特效方面有 Lut 颜色校正、Tonemap 映射、SSAO、镜面反射、Bloom 等常见特效,还有基于屏幕的反射 SSR(Screen Space Reflections)。
目前 Babylon 在渲染方面也是使用最传统的前向着色,如果要改造成类似 ClayGL 那样的延迟着色成本太高,兼容性也不好,所以最好的选择是用分簇来减少光源,这个想法在 2017 年就有提出,但然后就没有然后了。
除了在渲染方面的功能很多,Babylon 的周边工具也很丰富,最近还推出了类似 UE4 蓝图的材质编辑器。
我个人不喜欢使用蓝图这种方式来开发业务逻辑,因为复杂场景下信息密度太低了,大量线条看起来很累,但用蓝图来开发材质却很方便,因为着色器不好调试,而在蓝图中可以方便预览每个节点的效果,极大提升效率,不过也可能是我在着色器方面比较水。
不过 Babylon 的这个材质编辑器目前功能还比较弱,缺少流程控制语句等功能,和 Unreal Engine 差距很大。
另一个 Babylon 中最实用的工具是 Inspector,它可以像浏览器的开发者工具那样,选中场景中的某个节点,就能查看和修改它的属性和材质等,实时看到效果,比不断改代码调整方便多了。
另外 Babylon 还开发了可以直接运行在桌面的 BabylonNative,和 LayaNative 类似,基于封装了跨平台的渲染(基于 bgfx)、网络等接口,这样编译出一个桌面 Babylon 项目就不需要像 Electron 那样附带一个百兆的 Chromium 了,然而在桌面领域游戏引擎多如牛毛,竞争过于激烈,BabylonNative 看起来希望不大。
Babylon 最后一个亮点是正在开发 WebGPU 版本,而其他引擎都没开始做,所以等 WebGPU 发布后,Babylon 应该是首批支持的,将得到更多关注。
小结
前面说了那么多,如果没空看的话可以记一下我的看法:
- 对于一般 WebGL 开发,推荐使用 Babylon.js。
- 如果要支持微信小程序,最好用国内的 LayaAir 和 Cocos,但需要注意它们只是源码开放,并不是无条件免费使用,需要仔细阅读使用协议。
- 如果只想写原生 WebGL 特效,建议用 regl。
- 如想支持大量光源和后期特效,又不需要支持 iOS,用 Claygl。
- 如果熟悉 Unity,直接用它导出 WebGL 也是可行的。
后记:WebGL 为什么没火起来?
10 年前 WebGL 刚出来的时候我很期待,因为可以在 Web 上做出酷炫的三维效果了,看起来前景一片光明,然而现在除了小游戏,其它地方几乎没有人使用,也很少前端工程师了解,为什么呢?在我看来主要是这几方面的原因:
- WebGL 和前端开发差异性太大,前端工程师学 WebGL 相当于删号重练,除了繁琐的 API,图形学的数学知识也导致了门槛高,最基础的 MVP 就能劝退不少初学者,更别说后面的辐射度量及采样学知识了。
- 网速发展太慢,Web 不能像桌面游戏那样预先下载几十 G 的模型和材质,在 Draco 压缩技术出现前,随便一个模型文件就要十几 M,极大限制了 WebGL 能做的事情,没人愿意打开个页面还要先等几分钟加载。
- JavaScript 本身的性能限制,除了 JavaScript 虚拟机的消耗,在调用 WebGL API 的时候还需要经过一层转换,而很多浏览器是基于 ANGLE 支持 WebGL 的,在 ANGLE 中又会做一层检查,然后在 Windows 下还得将 GLSL 转成 HLSL,在 CPU 上要做的额外工作大大高于桌面程序。
- WebGL 2.0 相当缓慢的发展,使得 WebGL 引擎的技术也停滞不前,前面提到过 WebGL 1.0 缺少很多重要功能,比如 Draw Buffers、Occlusion queries 等现代渲染引擎优化性能所依赖的技术,而 WebGL 2.0 的发展又极其缓慢,尽管启动于 2013 年,但直到 2017 年才正式推出,要知道 OpenGL 的替代者 Vulkan 是在 2016 年推出的,所以 WebGL 2.0 还没发布就面临被淘汰,加上 Apple 的刻意打压,至今 Safari 在桌面和 iOS 上都不默认开启 WebGL 2.0,所以 WebGL 2.0 几乎没人使用,用也只能用那些支持面比较广的 WebGL 1.0 扩展功能。
- 经济方面的原因,前面提到的很多原因导致了 WebGL 只能开发小游戏,收益不高,收益不高投入就少,导致相关工具的缺乏,尤其是优秀的场景编辑器,UE4 之所以能实时渲染逼真画面,就是靠编辑器的烘焙和计算 Lightmass 来解决间接光照问题,编辑器开发成本极高,不如就只能等光线追踪普及并标准化,预计至少五年。
Web 的下一代技术是 WebGPU,它能极大提升性能,具体提升多少呢,在 WebGPU 官方 wiki 中拿了 Babylon 的例子:
https://www.babylonjs.com/demos/webgpu/forestwebglwww.babylonjs.comhttps://www.babylonjs.com/Demos/WebGPU/forestWebGPU.htmlwww.babylonjs.com我测试后发现 CPU 时间从 20ms 降到了 0.08ms,FPS 从 30 增加到了稳定 60,提升非常明显,是不是很期待 WebGPU 的未来?
然而这个对比并不公平,这里的性能提升不完全是 WebGPU 带来的,我猜是开发者在演讲前发现效果不明显,就先优化了一下,下面这段代码是 WebGPU 版本中多出的部分,如果加到 WebGL 版本中也能明显提升性能,因为每次循环的时候不需要重新计算了,可以显著减少 CPU 时间。
// After all the meshes creation, freeze as much as we can to improve perfs.
optimizeScene(scene);const optimizeScene = function(scene) {scene.freezeMaterials();scene.meshes.forEach((m) => {m.isPickable = false;m.alwaysSelectAsActiveMesh = true;m.freezeWorldMatrix();});setTimeout(() => {scene.freezeActiveMeshes();}, 1000);
}
后面有空我会单独介绍 WebGPU,赶紧关注哈哈。