doodlewind / blog

📝 My tech blog.
https://ewind.us
22 stars 2 forks source link

Beam 1.0 规划 #4

Closed doodlewind closed 5 years ago

doodlewind commented 5 years ago

Beam 从诞生起,就被寄托了成为团队首个自研 WebGL 基础库的重任,并随着业务一同快速地迭代演化。令我们欣慰的是,Beam 的成长跟上了业务需求的脚步。我们在多次针对性重构优化后,确实地获得了在工业级项目中,每个重要的指标(初始化速度、帧率、包体积等)都完全追平或优于开源方案的成果。虽然 Beam 仍然尚未发布首个 Major 版本,但实战的检验已经使得我们对渲染引擎的理解、掌控和预期都成熟得多了。为了促进这一正向循环,保证日后更大范围、更深度、更稳定的使用,是时候将 1.0 版本提上日程了。下面列出相关的规划。

1.0 目标

预期变更

Global / Element / State / Props 概念整合

着色插件涉及的这些核心抽象在 1.0 之前有些重叠而难以理解。现在这些概念得到了简化:

因此新设计下,渲染简化为了 State (Element / Global) -> Props -> WebGL 的过程。从而,上层 API 简化为 Renderer 的:

以及着色插件的:

使用方式形如:

class MyPlugin extends ShadePlugin {
  constructor () {
    // 配置 Schema
  }

  propsByElement (state) {
    return { propKeyA: state.keyA, propKeyB: state.keyB }
  }

  propsByGlobal (state) {
    return { propKeyC: state.keyC, propKeyD: state.keyD }
  }
}

const myPlugin = new MyPlugin()
const renderer = new Renderer(canvas, [myPlugin])

// 设置好供 propsByGlobal 获取的全局 State
renderer.setGlobalState('keyC', foo)
renderer.setGlobalState('keyD', bar)

// 两个不同的 Element,其 State 字段供 propsByElement 获取
const xElement = createElement({ keyA: fooX, keyB: barX }, MyPlugin)
const yElement = createElement({ keyA: fooY, keyB: barY }, MyPlugin)
renderer.addElement(xElement)
renderer.addElement(yElement)

// 根据 propKeyA / propKeyB / propKeyC / propKeyD 分组渲染物体
renderer.render()

Schema 合并

我们计划将 propSchemashaderSchema 合并为更简单的单个 PropSchema。PropSchema 分为 buffers / uniforms / textures 三种,每种下的字段 Key 均包含一个必填的 type 属性。形如:

import { SchemaTypes, ShadePlugin, Renderer } from '@gaoding/beam'

const { vec4, mat4, index, tex2D } = SchemaTypes
this.propSchema = {
  buffers: {
    pos: { type: vec4, n: 3 },
    color: { type: vec4 },
    index: { type: index } // 注意 index 类型
  },
  uniforms: {
    viewMat: { type: mat4 },
    projectionMat: { type: mat4 }
  },
  textures: {
    image: { type: tex2D, flip: true } // 注意不是 sampler2D
  }
}

这样的 API 既能符合直觉地与 GLSL 中的类型保持一致,又很易于处理 Index Buffer 和 Texture 这两种无法直接一一映射到着色器变量的资源内容。并且,sampler2DsamplerCube 只要给定了 tex2DtexCube 类型的 Schema 字段,相应纹理资源(图像、Canvas、TypedArray 等)就能由引擎自动管理。

开发者工具

由于 Beam 的核心代码几乎只关注 Happy Path,这导致了其虽然体积极轻(1000 行代码量级,仅有 regl 的约十分之一,Three 的几十分之一),但在调试时出错位置容易使人困惑。我们可以通过开发阶段的工具来改善这一问题。

Beam 的 Renderer 对象虽然紧密围绕 WebGL 设计,但所有操作 GL 实例的 Utils 代码都是从外部注入的。因此只需要替换掉这些耦合 GL 实例的 Utils,我们就能在其它环境中运行 Renderer。基于这一点,我们可以提供一个特殊定制的 DevRenderer,它不会执行实际渲染,但只要将上层代码改为使用这一 Renderer 执行,就能提供充分的 Schema 校验、反模式警告等特性。具体提示内容尚在规划中。

性能优化

1.0 中规划的性能优化包括如下:

它可以保证如下场景的性能达到最优:

欢迎在评论中 Review 或讨论目前的 1.0 规划与设计 :)

laoshu133 commented 5 years ago

「因此只需要替换掉这些耦合 GL 实例的 Utils,我们就能在其它环境中运行 Renderer」 这是不是意味着,如果 API 足够简单即可以方便的 bridge 到 G3D 之类的引擎上?

doodlewind commented 5 years ago

目前较设计之初,已实现的改动:

规划的改动: