跳到主要内容

渲染通道

RenderPass 是向渲染目标绘制的帧通道。执行时,其绘制调用记录到对应目标的物理渲染通道中。通常物理通道在其执行前开始、执行后结束;也可能与相邻且渲染到同一目标的通道共享,参见共享渲染目标的通道。

RenderPass 的派生类​

RenderPass 是执行绘制的通道基类。引擎为常见绘制提供派生类,可直接使用,也可作为自定义通道的基类:

类用途
RenderPassShaderQuad对渲染目标或其局部的每个像素运行着色器,例如后处理效果。
RenderPassForward使用相机渲染场景图层。

要记录自己的绘制调用,可直接继承 RenderPass,参见编写通道。本页其余内容适用于所有渲染通道,包括派生类。

配置通道​

创建通道后,调用 init 并传入要绘制到的渲染目标;传入 null 则绘制到后备缓冲区。在渲染及设置清除、存储行为之前,必须完成初始化:

const pass = new MyPass(app.graphicsDevice);
pass.name = 'MyPass';
pass.init(renderTarget);

通道不拥有渲染目标,也不会销毁它。再次调用 init 可切换到附件不同的目标。对于布局相同(颜色缓冲区、深度、模板和采样数相同)的目标,也可直接赋给 renderTarget,例如每帧交替使用两个目标。

调整渲染目标大小​

向 init 传入选项时,通道每帧还会将目标调整到指定纹理、渲染目标或后备缓冲区的尺寸,并乘以 scaleX 和 scaleY:

// a render target half the size of the back buffer
pass.init(renderTarget, {
resizeSource: null,
scaleX: 0.5,
scaleY: 0.5
});

// a render target the size of the texture the pass processes
pass.init(renderTarget, { resizeSource: sourceTexture });

选项会被复制。之后要更改,请设置通道的 resizeSource、scaleX 和 scaleY。例如处理的纹理被替换时,更新 resizeSource。默认 resizeSource 为 undefined,保持目标尺寸不变。

目标在构建帧时、通道的 frameUpdate 中调整大小,通道更新后即可取得新尺寸。尺寸变化的那一帧,在它之前更新的通道仍看到旧尺寸。

清除附件​

渲染目标的附件包括颜色、深度和模板缓冲区。默认物理通道加载已有内容,当前通道在其上绘制。若要从统一值开始,应清除附件:

pass.setClearColor(Color.BLACK);
pass.setClearDepth(1);
pass.setClearStencil(0);

通道完全覆盖附件时,在分块移动 GPU 上清除也比加载更快,因为无需从内存读取旧内容。setClearColor 清除所有颜色缓冲区,也可只清除指定索引的缓冲区。

存储附件​

物理渲染通道结束时,会保留渲染到目标纹理的内容,以便采样或由后续通道加载:包括颜色缓冲区(多重采样目标会先解析)和深度缓冲区纹理。其他附件被丢弃,包括已解析的多重采样颜色样本、模板,以及目标内部创建的深度缓冲区。

如果同帧后续通道向相同目标绘制并加载这些附件,帧会自动存储它们。独立渲染通道时,应通过 setStoreColor、setStoreDepth 和 setStoreStencil 指定存储。

具有 mipmap 的颜色缓冲区也会在通道结束时生成 mipmap。多个通道绘制到同一目标时,只在最后一个之后才需要 mipmap,因此应对其他通道调用 setGenerateMipmaps 禁用生成:

firstPass.setGenerateMipmaps(false);

init 会将清除、存储和 mipmap 设置重置为默认值,因此应在初始化之后设置。

共享渲染目标的通道​

一帧中连续绘制到同一目标的通道会合并为一个物理渲染通道,避免中间存储和加载附件。如果两者之间必须发生某些操作,则无法合并,例如后一个通道的清除、两者之间的前置或后置通道,或 onBefore、onAfter 回调。

编写通道​

子类在 execute 中记录绘制调用,此时物理渲染通道处于活动状态。可覆盖 postInit,在初始化后配置清除;覆盖 frameUpdate,在每帧渲染前更新,并调用 super.frameUpdate() 保留尺寸调整。还应在 destroy 中释放自己创建的对象,并调用 super.destroy():

// A pass drawing a quad with a shader, covering its whole render target
class MyPass extends RenderPass {
constructor(device, shader) {
super(device);
this.quadRender = new QuadRender(shader);
}

postInit() {
// the quad covers the whole target, so clear it instead of loading its content
this.setClearColor(Color.BLACK);
}

execute() {
// the default draw states suit a quad: no blending, and no depth test or write
this.device.setDrawStates();
this.quadRender.render();
}

destroy() {
this.quadRender.destroy();
super.destroy();
}
}

对于使用着色器绘制的四边形,RenderPassShaderQuad 已完成这些工作,并配置渲染状态。

向纹理渲染一次​

若只在内容变化时才向纹理渲染,可在帧外调用 render,独立渲染通道:

const renderTarget = new RenderTarget({ colorBuffer: texture, depth: false });
const pass = new MyPass(app.graphicsDevice, shader);
pass.name = 'MyPass';
pass.init(renderTarget);

// draw into the texture
pass.render();

// destroy the pass, and then the render target, which the pass does not own
pass.destroy();
renderTarget.destroy();

示例​

两个未附加到任何相机的 RenderPass 子类生成材质纹理:一张图案,以及根据其高度生成的法线贴图。它们从 prerender 事件独立渲染;动画期间每帧执行,否则仅在设置变化时执行。

程序化纹理