多渲染目标
多渲染目标功能允许同时渲染到多张纹理。本页介绍其实现、配置和使用示例,建立在渲染目标所述概念之上。
PlayCanvas 支持的所有设备(WebGL2 和 WebGPU)均支持 MRT。通过 GraphicsDevice.maxColorAttachments 检查当前设备可用的颜色附件数量。通常支持 8 个。
多渲染目标具有以下限制:
- 同一多渲染目标的所有颜色附件必须具有相同宽度和高度。
- 颜色附件清除为
CameraComponent.clearColor,除非使用CameraComponent.setClearColor为其设置独立清除颜色,见下文清除颜色。 - 所有颜色附件使用
BlendState指定的相同写入遮罩和 alpha 混合模式;如果设备报告支持GraphicsDevice.supportsIndependentBlending,则可用BlendState.setAttachment为附件单独设置。 - 双源混合要求恰好一个颜色附件,因此不能与 MRT 结合。
如何使用 MRT
创建要渲染到的纹理:
const createTexture = name => new pc.Texture(app.graphicsDevice, {
name,
width: 512,
height: 512,
format: pc.PIXELFORMAT_RGBA8,
minFilter: pc.FILTER_LINEAR,
magFilter: pc.FILTER_LINEAR,
addressU: pc.ADDRESS_CLAMP_TO_EDGE,
addressV: pc.ADDRESS_CLAMP_TO_EDGE
});
const texture0 = createTexture('RT-texture-0');
const texture1 = createTexture('RT-texture-1');
const texture2 = createTexture('RT-texture-2');
将它们封装为渲染目标:
const renderTarget = new pc.RenderTarget({
name: 'MRT',
colorBuffers: [texture0, texture1, texture2],
depth: true,
origin: pc.RENDERTARGET_ORIGIN_TOP,
samples: 2
});
创建用于渲染到 MRT 的相机实体:
const entity = new pc.Entity('MRTCamera');
entity.addComponent('camera', {
// negative priority => rendered before the main camera each frame
priority: -1,
renderTarget
});
app.root.addChild(entity);
// set the camera to use a custom shader pass called MyMRT
entity.camera.setShaderPass('MyMRT');
清除颜色
相机开始渲染时,所有颜色附件都清除为相机的 clearColor。附件保存不同类型的数据时,可将附件索引传给 CameraComponent.setClearColor,分别设置清除颜色。对于整数格式附件,颜色分量就是清除时使用的整数值。
// attachment 0 keeps clearing to entity.camera.clearColor
// clear the normals attachment to a neutral normal, and the gloss attachment to black
entity.camera.setClearColor(1, new pc.Color(0.5, 0.5, 1, 1));
entity.camera.setClearColor(2, new pc.Color(0, 0, 0, 1));
传入 null 移除附件的独立清除颜色,使其重新使用 clearColor。通过 CameraComponent.getClearColor 读取附件的清除颜色。
逐附件清除颜色只适用于相机开始渲染时的清除。渲染过程中的清除,例如由启用了 clearColorBuffer 的 Layer 执行,或相机的 rect 未覆盖整个渲染目标时,不支持逐附件颜色,使用 MRT 时最好避免。
标准材质
使用 StandardMaterial 渲染到多渲染目标(MRT)时,覆盖 outputPS 着色器代码块,向额外的颜色缓冲区输出值。为项目目标的每种着色语言提供代码块:WebGL2 使用 GLSL,WebGPU 使用 WGSL。通过 Material.getShaderChunks,将两个版本应用到目标实体渲染组件的每个材质:
// GLSL output chunk (used on WebGL2)
const glslChunk = `
#ifdef MYMRT_PASS
// output world normal to target 1
pcFragColor1 = vec4(litArgs_worldNormal * 0.5 + 0.5, 1.0);
// output gloss to target 2
pcFragColor2 = vec4(vec3(litArgs_gloss), 1.0);
#endif
`;
// WGSL output chunk (used on WebGPU)
const wgslChunk = `
#ifdef MYMRT_PASS
// output world normal to target 1
output.color1 = vec4f(litArgs_worldNormal * 0.5 + 0.5, 1.0);
// output gloss to target 2
output.color2 = vec4f(vec3f(litArgs_gloss), 1.0);
#endif
`;
// `targetEntity` is the entity (or hierarchy root) whose materials you want rendered through MRT
const renders = targetEntity.findComponents('render');
renders.forEach((render) => {
render.meshInstances.forEach((meshInstance) => {
const material = meshInstance.material;
material.getShaderChunks(pc.SHADERLANGUAGE_GLSL).set('outputPS', glslChunk);
material.getShaderChunks(pc.SHADERLANGUAGE_WGSL).set('outputPS', wgslChunk);
material.shaderChunksVersion = '2.8';
});
});
上述代码块只写入颜色目标 1 和 2,目标 0(GLSL 中的 pcFragColor0,WGSL 中的 output.color0)仍接收标准前向通道输出。如果也要覆盖它,可在同一代码块中写入。
自定义着色器
使用完全自定义的片元着色器而非 StandardMaterial 时,直接向 GLSL 的 pcFragColor0...pcFragColor7 或 WGSL 的 output.color0...output.color7 写入所需值。
若要将修改限制在特定相机中,使用该相机的着色器通道宏作为条件。宏名称为通道名大写后加 _PASS,因此 setShaderPass('MyMRT') 会在着色器中启用 MYMRT_PASS。
示例
引擎示例中提供完整可运行的 Multiple Render Targets 示例:通过自定义着色器通道渲染棋盘,将世界法线和光泽度写入额外颜色目标,每个目标具有独立清除颜色,并在屏幕上作为独立纹理显示。