跳到主要内容

帧通道

在 PlayCanvas 中,帧图的每个逻辑通道都是一个帧通道:它是引擎对象,在帧渲染时记录该通道的工作。引擎自身也用这种方式渲染:阴影贴图、各相机场景和后处理都是帧通道。你也可以添加自定义帧通道,向纹理渲染、处理相机渲染结果,或运行计算着色器。

每帧,引擎按顺序将帧通道收集到帧图中,再依次执行,开始和结束它们所需的物理通道。无需自行开始或结束物理通道。

通道类​

每个帧通道都是 FramePass 或其派生类的实例。两种派生类对应两种物理通道:用于渲染的 RenderPass,以及用于计算着色器的 ComputePass:

类用途
FramePass基类。用于不需要物理通道的工作,或组织其他帧通道。
RenderPass渲染到渲染目标或后备缓冲区。
ComputePass在帧中指定位置派发计算着色器。

RenderPass 是逻辑渲染通道,将绘制调用记录到对应目标的物理渲染通道中。通常引擎为每个 RenderPass 开始独立物理通道,并在其结束后关闭。当连续的 RenderPass 对象渲染到同一目标时,会共享单个物理通道,参见帧图。类似地,引擎将 ComputePass 的派发记录到物理计算通道。

引擎提供 RenderPass 的派生类来完成常见绘制,例如对渲染目标的每个像素运行着色器,或渲染场景。参见渲染通道。

帧通道的生命周期​

帧通道在参与的每帧中经历两个阶段。构建帧时,在任何渲染发生前,引擎更新通道,确定本帧要渲染哪些通道。随后按顺序渲染它们。

子类通过覆盖以下方法完成工作:

  • frameUpdate:在任何渲染之前更新本帧通道,例如调整渲染目标大小、设置读取的纹理,或增减前置和后置通道。
  • before 和 after:围绕通道工作执行。
  • execute:记录工作。对于 RenderPass,它在对应目标的物理渲染通道内部执行。

通过 onBefore 和 onAfter 回调,无需编写子类即可接入通道:在执行前设置参数或清空写入缓冲区,在执行后立即回读结果:

pass.onBefore = () => {
buffer.clear();
};

pass.onAfter = () => {
buffer.read().then((data) => {
// the results arrive a few frames later
});
};

enabled 与 executeEnabled​

两个属性可在生命周期的不同阶段关闭通道:

  • 将 enabled 设为 false,会在构建帧时跳过该通道及其前置和后置通道,也不调用 frameUpdate。必须在构建帧之前更改;通道不能在自己的 frameUpdate 中禁用自身。
  • 将 executeEnabled 设为 false,仅跳过 execute。通道仍更新,前置和后置通道仍渲染,before、after、onBefore 和 onAfter 仍调用。最迟应在 frameUpdate 中设置。

前置和后置通道​

每个通道都有 beforePasses 和 afterPasses,在该通道每次参与渲染时,分别紧邻其前后执行:

由此可以嵌套通道,普通 FramePass 也能容纳一组共同启用和禁用的通道:

// a group of two blur passes: disabling the group skips both
const blur = new FramePass(app.graphicsDevice);
blur.name = 'Blur';
blur.beforePasses.push(horizontalBlurPass, verticalBlurPass);

blur.enabled = false;

向相机添加通道​

相机先执行前置通道,再执行自身渲染(可用自定义帧通道替换),最后执行后置通道。参见相机通道。

独立渲染通道​

调用 render 也可在帧外渲染通道,例如向纹理仅渲染一次。它会执行 before、onBefore、execute、after 和 onAfter,但不经过帧准备过程:不调用 frameUpdate,也不渲染前置和后置通道。

pass.render();

通道所有权​

没有其他对象会替你销毁通道。相机不销毁其列表中的通道,通道也不销毁前置和后置通道。创建通道的代码应先将其从所有已加入的列表移除,再调用 destroy 销毁。销毁后不得再使用。通道也不会销毁传给它的渲染目标或着色器等对象。

通过 name 设置的通道名称会在 GPU 性能分析和图形调试器中标识它。请显式设置,因为默认的类名在压缩构建中会缩短。