Skip to content

渲染管线 ​

每个 Engine 内部独占一个 EffectComposer、一个支持 Multi-View 的默认 main 和一个 OutputPass。业务不接触这些 identity,只通过 Pass Definition 借入自己创建的原生 Three.js Pass。

两个阶段 ​

Pipeline 只暴露:

阶段用途
after-render默认 main 之后、颜色输出之前的 working-space 后处理
after-outputOutputPass 之后的 sRGB 后处理或直接 Canvas 绘制

stage 默认是 after-render。同一 stage 和执行 lane 内按 Definition 的 numeric order 升序,再按加入顺序稳定排序。

默认 auto 模式固定执行:

text
DefaultMain → after-render → OutputPass 上屏 → Canvas Pass

main 和 OutputPass 都不能由业务替换。auto 模式不会执行 after-output 的后处理链 contribution,但仍会执行显式的 Canvas contribution。

创建后处理 ​

ts
import { createPass } from '@tripo3d/engine';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js';

const rgb = new ShaderPass(RGBShiftShader);

const ColorShift = createPass('color-shift', ({
  onDestroy,
  onResize,
  usePass,
}) => {
  usePass(rgb);
  onResize(({ width, height, pixelRatio }) => {
    console.log('logical size', width, height, pixelRatio);
  });
  onDestroy(() => rgb.dispose());
  return rgb;
}, {
  enabled: false,
  order: 20,
  stage: 'after-render',
});

engine.pipeline.addPass(ColorShift);
engine.pipeline.setPassEnabled(ColorShift, true);

usePass 只能在同步 setup 中调用,可按调用顺序借入多个 Pass。Pipeline 会把 physical size 传给每个原生 Pass 的 setSize,并共同读取 Definition enabled 与原生 pass.enabled。

Pipeline 不 dispose borrowed Pass;资源由创建它的业务 owner 释放。

Manual 输出与 FXAA ​

需要在 OutputPass 后继续读取 sRGB buffer 时,切换为 manual 模式,并让最终 contribution 显式声明 presentation:

ts
const Fxaa = createPass('fxaa', ({ onDestroy, onResize, usePass }) => {
  const pass = new ShaderPass(FXAAShader);

  onResize(({ width, height, pixelRatio }) => {
    pass.uniforms.resolution!.value.set(
      1 / (width * pixelRatio),
      1 / (height * pixelRatio),
    );
  });
  usePass(pass, { present: true });
  onDestroy(() => pass.dispose());
}, {
  enabled: false,
  stage: 'after-output',
});

engine.pipeline.addPass(Fxaa);

// 同步切换 FXAA owner 与其所需的 manual presentation 模式。
function setFxaaEnabled(enabled: boolean) {
  engine.pipeline.setPassEnabled(Fxaa, enabled);
  engine.pipeline.setOutputMode(enabled ? 'manual' : 'auto');
}

manual 模式固定执行:

text
DefaultMain
→ after-render
→ OutputPass 写入 sRGB buffer
→ after-output 后处理链
→ 最终 present Pass 上屏
→ Canvas Pass

如果存在有效颜色工作,但最终 effective 后处理 Pass 没有声明 present: true,Composer 结果不会自动上屏。Engine 不提供 fallback,也不会推断 presenter;它会在每个连续的缺失 presenter 异常周期输出一次 console.warn,提示业务修复组合。Canvas contribution 不会替代该 presenter。

直接绘制 Canvas ​

Gizmo、HUD 等不需要读取或改写整张画面的内容,可以复用同一个 usePass:

ts
const Gizmo = createPass('gizmo', ({ onDestroy, usePass }) => {
  const pass = new GizmoPass();
  usePass(pass, { target: 'canvas' });
  onDestroy(() => pass.dispose());
}, {
  order: 100,
  stage: 'after-output',
});

Canvas Pass 是只写的 direct-draw lane:它在默认 framebuffer baseline 上以 renderToScreen = true 直接执行,不进入后处理链、不 swap,也不需要通过 CopyShader 复制整张画面。

为兼容 Three.js Pass.render() 方法签名,Pipeline 仍会传入 Composer 当前的 writeBuffer / readBuffer,但 Canvas lane 不为它们的画面内容或颜色空间提供任何稳定契约。尤其在切换 output mode 或 Composer ping-pong 状态变化后,它们可能分别指向输出变换前的 working-space 内容、输出变换后的 output-encoded 内容或旧内容;Canvas Pass 不得采样或依赖这两个参数。

需要读取整张画面的 Pass 必须使用 Composer target:读取输出变换前的 working-space 内容时放在 after-render;读取输出变换后的 output-encoded 内容时放在 after-output,并将 Pipeline 设为 manual 模式。

target 决定物理执行 lane:后处理链先完整执行,Canvas lane 随后执行。order 只比较同 stage、同 lane 的 contribution。Engine 不分析、拒绝或修正跨 lane 顺序;业务必须保证最终 presenter 是最后一个有效后处理 Pass,并只把确实需要在 presentation 后绘制的 Pass 声明为 Canvas target。

每帧准备 ​

ts
const Dynamic = createPass('dynamic', ({ onPrepareFrame, usePass }) => {
  const pass = new MyPass();
  usePass(pass);

  onPrepareFrame(({ elapsed }) => {
    pass.strength = computeStrength(elapsed);
  });
});

prepare 按 stage/order/sequence 执行。全部 prepare 完成后才冻结当帧计划,因此 prepare 可以通过当前 Definition 的 setEnabled 改变本帧是否执行;planning/render 期间不能再改 Pipeline topology、enabled 或 output mode。

Render target 能力 ​

ts
const engine = createEngine({
  renderer: { antialias: false },
  pipeline: {
    samples: 0,
    stencil: false,
  },
});

默认 Canvas antialias 为 false,默认 Pipeline samples 为 0,因此没有 Canvas MSAA、离屏 MSAA 或 resolve blit。两项配置相互独立;业务仍可分别显式开启。samples 必须是非负整数,stencil 默认为 false。

性能模型 ​

  • stage bucket、排序和 View snapshot 在 add/remove/order mutation 冷路径重建;
  • frame context、后处理链、Canvas chain 与 plan result 跨帧复用;
  • stable frame 不 sort/filter,不创建业务 owner 数组;
  • 默认无 FXAA 路径只执行 main、OutputPass 和可选的直接 Canvas Pass;
  • 业务应避免每帧 add/remove Definition,动态开关使用 enabled。

下一步 ​

基于 MIT 协议发布(内部使用)