WebGL渲染器设计思考

2,581 阅读6分钟

索引

  • 引子
  • 功能
  • 架构
  • 风格
  • 应用
  • 未来

引子

那些活跃的开源项目

  • Babylon.js

github.com/BabylonJS/B…

一个==完整==的JavaScript框架,用于构建HTML5,WebGL,WebVR和Web Audio的3D游戏和体验。

  • PlayCanvas

github.com/playcanvas/…

PlayCanvas是一个开源游戏引擎。它使用HTML5和WebGL在所有==现代==浏览器中运行游戏和其他==交互==式3D内容,而无需插件。

  • ClayGL

github.com/pissang/cla…

ClayGL是一个用于构建==可扩展==Web3D应用程序的WebGL图形库。

它易于使用,可配置高质量图形效果。受益于==模块化==和tree shaking,它可以缩小到22k(gzip),用于基本的3D应用程序。

目前主要应用到echarts中。

  • Three.js

github.com/mrdoob/thre…

该项目的目的是创建一个==易于使用==,==轻量级==的3D库。该库提供Canvas 2D,SVG,CSS3D和WebGL渲染器。

Zen3D一开始模仿threejs的WebGL渲染库

初始动机:学习WebGL,学习3D渲染。

从学习相机的==矩阵变换原理==与==简单光照模型==开始。

图书推荐:WebGL编程指南

项目地址:github.com/shawn0326/z…

Three.js 最火

  • 年头最早?最稳定?最活跃?
  • 只做渲染,不做其它

功能

大城小胖提出的钳子锤子理论

2016年,国内Html5游戏引擎竞争激烈:

  • cocos-html5 (后改名cocos-js)
  • Egret Engine
  • LayaBox

各方以推出完善工具流为目标。

image

“你要是想用我的钳子,必须也买我的锤子”这是一种霸道做法。

他为了宣扬自己的理论,在github上发布了一个叫The Best JavaScript Game Framework的项目。

他的核心思想是:

Do More with Less. 做的事情越少,能做的事情越多。

局限性:

  • 仅适用于架构程序员。对脚本程序员或非程序员,需要全面的配套服务。但这是上层需求,是“工具”应该做的事而非底层库。
  • 当然不是单纯越少越好,满足通用需求是核心价值。

结论:Threejs由于只实现了核心功能,不搞x大包x大揽,所以能应用到更多的领域,另外,也让它更容易理解和使用。

问题:Threejs的核心x价值是什么?

Three的核心价值

画一个三角形需要这么多代码~

image

重新审视Three.js

只干了两件事:

  • Graph Tree 场景树管理
  • Render 渲染

实际上在做什么:

graph TB
3D场景树 --> 一维渲染列表
3D场景树 --> 相机
相机 --> 2D图像
一维渲染列表 --> 2D图像

场景树管理模块

无关渲染API

  • 解决的问题
  1. 怎样描述3D场景(人容易看懂)
  2. 怎样描述渲染列表(机器容易看懂)

3D场景的描述规范:

graph TB
场景-->物体1
场景-->物体2
物体1-->物体3
物体1-->物体4

物体的描述规范:

graph TB
物体-->形状
物体-->材质

渲染列表:

[{画物体1}, {画物体3}, {画物体2}, ...]

渲染器模块

利用WebGL渲染API进行渲染。

graph TB
读取顶点数据-->执行顶点着色器
执行顶点着色器-->组装图元
组装图元-->光栅化图元
光栅化图元-->执行片段着色器
执行片段着色器-->写入帧缓冲区

单次渲染(one pass)流程与WebGL接口强相关,相对固定。

架构

反思

Threejs在架构上有什么改进的空间?

threejs的Renderer包含了太多的东西。

也许是因为要兼容多种渲染模式造成的。

Three.js封装的Renderer

当你调用render的时候:

renderer.render(scene, camera, target);

实际上做了这些事:

  1. 更新场景中所有物体的矩阵
  2. 生成渲染列表(分类,收集, 视锥裁剪,排序...)
  3. 执行渲染

显然threejs将场景树的更新与渲染做到了一个函数中。

image

后期与延迟渲染中常常遇到的场景

graph LR
场景-->物体
物体-->形状
物体-->材质1

在后期或延迟渲染中,可能需要替换每个物体的材质(shader),并执行一次渲染

graph LR
场景-->物体
物体-->形状
物体-->材质2

根据物体属性不同,可能替换的材质还不一样

Threejs Renderer做的事情太多了

scene.traverse(object => {
    // change material1
});

renderer.render(scene, camera, target);

scene.traverse(object => {
    // change material2
});

renderer.render(scene, camera, target);

// ...

实际上:

  1. 遍历场景替换材质1(遍历浪费)
  2. 更新场景中所有物体的矩阵
  3. 生成渲染列表(分类,收集, 视锥裁剪,排序...)
  4. 执行渲染
  5. 遍历场景替换材质2(遍历浪费)
  6. 更新场景中所有物体的矩阵(浪费)
  7. 生成渲染列表(分类,收集, 视锥裁剪,排序...)(浪费)
  8. 执行渲染
  9. ...

如果拆分开Renderer

scene.updateMatrix();

const renderList = getRenderList(scene, camera);

// pass 1
renderer.render(renderList, camera, {
    ifRender: item => { 
        return true;
    },
    getMaterial: item => {
        // 这里可以通过item.object动态判断使用哪种材质
        return material1;
    }
});

// pass 2
renderer.render(renderList, camera, {
    ifRender: item => { 
        return true;
    },
    getMaterial: item => {
        // 这里可以通过item.object动态判断使用哪种材质
        return material2;
    }
});

实际上:

  1. 更新场景中所有物体的矩阵
  2. 生成渲染列表(分类,收集, 视锥裁剪,排序...)
  3. 使用材质1渲染
  4. 使用材质2渲染

能很方便地实现后期处理,前向渲染器/延迟渲染器。

项目风格

项目的语言选择与是否激进。

风格

  • ES6但不选择 TypeScript CoffeeScript 等编译语言。

Babylon采用ts开发,一定程度上减小了开发者的受众人群。

  • 优先使用新特性(Extensions & WebGL2)但适当兼容。

Babylon在对WebGL2的支持上相对较好,因此它有更强大的粒子系统,更好的渲染效果,更高的性能。

当前大部分浏览器已经支持WebGL2,但很少渲染器能很好利用新特性并做到适当兼容。

WebGL2的粒子 babylon webgl2 babylon webgl2 color Grading

应用

  • 3D开发引擎 babylon
  • ECS架构的重度3D开发引擎 PlayCanvas,Unity
  • 非专业程序员使用的脚本绘图工具 P5.js
  • 模型展示器,可以实现方便的后期处理模块 ClayGL Sketchfab
  • WebGL新特性探索
graph BT
渲染器-->3D游戏引擎
渲染器-->3D应用引擎
渲染器-->模型展示器
渲染器-->绘图画板
渲染器-->等等

未来

  • 与上游工具的联动
graph TB
用户 --> 粒子编辑工具
用户 --> 材质编辑工具
用户 --> 场景搭建工具
用户 --> 逻辑组织工具
粒子编辑工具 --> 3D场景树
材质编辑工具 --> 3D场景树
场景搭建工具 --> 3D场景树
逻辑组织工具 --> 3D场景树
3D场景树 --> 一维渲染列表
3D场景树 --> 相机
相机 --> 2D图像
一维渲染列表 --> 2D图像
  • 安利

Blender

Blender电影

Blend4Web

Blend4Web奶牛场例子