https://zhuanlan.zhihu.com/p/162878354
如何选择 WebGL 框架和引擎?
知道得越多,不知道的就更多了
数据可视化Sugar-百度智能云
cloud.baidu.com/product/sugar.html
引擎类型
WebGL 框架和引擎按照定位可以分成这三种类型:
WebGL 封装,定位是简化 WebGL 开发,最大的特点是必须自己写 GLSL 才能用。
渲染引擎,定位是三维物体及场景展示,一般会抽象出场景、相机、灯光等概念,上手门槛低,不需要自己写 GLSL。
游戏引擎,定位是游戏开发,在前面的渲染引擎基础上,还提供了骨骼动画、物理引擎、AI、GUI 等功能,以及可视化编辑器来设计关卡,支撑大型游戏的开发。
WebGL 封装
先说 WebGL 封装,这种库主要解决的问题是 WebGL 的 API 过于繁琐。
WebGL 源自 OpenGL,它最早可以追溯到 1992 年,那个时候还是以 C 这种面向过程式的语言为主,所以 OpenGL 的 API 也是过程式的,对于熟悉面向对象的开发者来说,它的代码看起来冗长且可读性差,因此有必要对其进行封装和简化。
twgl.js
twgl.js
github.com/greggman/twgl.js
twgl.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
regl
github.com/regl-project/regl
和 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/ogl
github.com/oframe/ogl
OGL 的定位比较特别,它有点介于 WebGL 封装和渲染引擎之间,比如它最简单的这个例子:
impor