💡 本系列文章收录于个人专栏 ShaderMyHead。
💡 本文案例可以在 Github 上进行演示。
在某些场景可能需要创建多个摄像头 + RenderTexture 文件来叠加着色器效果,例如在《高斯模糊的实现》一文中,我们需要通过这种麻烦的操作来应用 gaussian-blur-vertical.mtl
材质,才能在原本应用了水平方位高斯模糊材质的基础上,进一步在垂直方位应用高斯模糊。
按照以往的方案,如果需要叠加 N 个着色器,我们就得创建 N 个摄像头、RenderTexture 文件、绘制前一个摄像头捕获画面的 SpriteFrame 节点,这会让操作变得麻烦,层级管理器上的节点也会变得杂乱和难以阅读,Draw Call 数量也会跟着变多。
Cocos Creator 提供了摄像机后处理(Post Process)的能力,通过该能力可以极大减少着色器应用的复杂度。
一、后处理功能的使用
假设场景上存在一个仅捕获 DEFAULT
层级的摄像机 CameraForDefault
,和一张处于 DEFAULT
层级的绿叶背景图 BG
节点:

假设我们希望对绿叶背景图做 4 次高斯模糊(水平和垂直方向上各 2 次,且需要按顺序执行),并再染上 30% 透明度的绿色,这番操作会涉及三个着色器 ------ 水平模糊着色器、垂直模糊着色器、染色着色器,它们的执行次序预期为:
rust
水平模糊着色器 -> 垂直模糊着色器 -> 水平模糊着色器 -> 垂直模糊着色器 -> 染色着色器
按照以往的方案,我们需要额外创建 5 个摄像头用于生成 RenderTexture、5 个 SpriteFrame 节点用于缓存和绘制各步骤处理后的纹理,整个操作会很繁琐和低效。
启用摄像机的后处理功能,则仅需要新增一个节点用于绘制多个着色器处理后的画面。
1.1 启用后处理功能
后处理功能依赖于自定义渲染管线,在 Cocos Creator 3.8.6 中需要先在「项目设置 -> 图像设置 -> 新渲染管线」处确保勾选「后处理模块」,且必须填写一个自定义的管线名称 (如下图所示,填写了一个叫 Customize
的名称):

接着我们勾选上摄像机属性检查器界面的 Use Post Process
,表示对此摄像机启用自定义的后处理管线:

💡 Cocos Creator 官方目前暂停了对后处理模块的维护(因此标记为废弃),不排除未来会调整后处理功能的使用形式。
💡 Cocos Creator 官方也提供了自定义渲染管线新方案(实验性质)的 Demo,个人研究后不推荐使用(需要自行开发组件脚本,使用起来比较复杂,且极容易报错)。
我们再新增一个位于 DEFAULT
层级的空节点 PostProcessForDefault
,它将用于绘制后处理管线处理后的最终画面。我们将该节点拖入摄像机的 Post Process
处进行绑定:

这样 Cocos Creator 会把摄像机捕获的离屏画面交给 PostProcessForDefault
节点的后处理组件(下一小节会添加)去处理。
1.2 新增后处理组件
PostProcessForDefault
节点需要添加专门的组件,才能接收和处理来自 CameraForDefault
摄像机捕获的离屏画面。
我们为其添加名为 BlitScreen
的组件(该组件依赖于 PostProcess
组件,因此会同步添加上 PostProcess
组件),在 BlitScreen
的组件的 Materials
处,可以自行新增多个材质插槽:

💡
BlitScreen
主要负责维护一个有序的后处理材质列表,PostProcess
组件作为后处理管理器,为每个摄像机提供对应的后处理实例配置。在渲染流程中,
PostProcess
会获取摄像头捕获的画面,将其交由BlitScreen
维护的材质列表进行链式依序处理,每个材质的输出作为下一个材质的输入,最终形成完整的后处理效果链。
留意去掉 PostProcess
组件的 Global
选项,若勾选了表示它会作为全局默认的后处理设置,将应用于所有开启了后处理功能但未绑定 PostProcess
节点的摄像机。
另外我们创建 5 个材质槽位,用来依次放置需要的着色器对应材质:

1.3 后处理着色器
后处理管线使用的着色器语法与我们之前编写的基本一致,但有两个关键点需要留意:
- CCEffect 的
passes
中必须声明pass: post-process
,表示该着色器通道(pass)的类型为后处理通道; - 当前纹理变量不再是
cc_spriteTexture
,而是inputTexture
,且需要使用#pragma rate inputTexture pass
预处理器指令将其指向 BlitScreen 在通道中传递的纹理名称。
依照上述两条规则,「给画面染上 30% 透明度的绿色」的着色器(green-screen.effect
)代码为:
js
CCEffect %{
techniques:
- name: opaque
passes:
- vert: green-screen-vs
frag: green-screen-fs:frag
pass: post-process # 新增声明
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
depthStencilState:
depthTest: false
depthWrite: false
}%
CCProgram green-screen-vs %{
precision highp float;
in vec3 a_position;
in vec2 a_texCoord;
out vec2 v_uv;
void main () {
vec4 pos = vec4(a_position, 1.0);
gl_Position = pos;
v_uv = a_texCoord;
}
}%
CCProgram green-screen-fs %{
precision highp float;
in vec2 v_uv;
#pragma rate inputTexture pass
uniform sampler2D inputTexture; // 留意当前纹理不再是 cc_spriteTexture
vec4 frag () {
vec4 color = texture(inputTexture, v_uv);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
return mix(color, green, 0.3);
}
}%
按规则修改上篇文章的高斯模糊着色器代码后,创建 hor-blur.mtl
、ver-blur.mtl
、green-screen.mtl
三个对应的材质,并将它们拖入 BlitScreen
组件的材质插槽:

在 Cocos Creator 编辑器中执行项目,通过控制各材质下的 Enable
选项,可以决定是否启用该材质:

可以看到后处理管线会将 BlitScreen
组件中的着色器材质依序逐个应用,最终得到了我们想要的画面处理效果:
rust
水平模糊 -> 垂直模糊 -> 水平模糊 -> 垂直模糊 -> 染上30%绿色
二、多摄像机后处理
若场景存在多个摄像机,每个摄像机可以走同样的方式启用互相独立的后处理管线。
2.1 绑定
我们新增一个 CameraForUI2D
摄像机用于拍摄 UI_2D
层级的内容,以及一个 PostProcessForUI2D
节点。
摄像机勾选 Use Post Process
后,将 PostProcessForUI2D
节点绑定到摄像机的 Post Process
处:

同时新增一个层级位于 UI_2D
的 Label(只会被 CameraForUI2D
摄像机捕获):

我们新建一个着色器和对应的材质,来给这个 Label 染上 30% 透明度的红色,其中着色器文件(red-screen.effect
)与前文的 green-screen.effect
内容基本一致,只是将片元着色器中的混合颜色改为红色:
scss
vec4 frag () {
vec4 color = texture(inputTexture, v_uv);
// 混合的颜色为红色
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
return mix(color, red, 0.3);
}
最后一步是为 PostProcessForUI2D
节点添加 BlitScreen
组件并绑定对应的材质:

此时新增的摄像机也拥有了自己独立的后处理管线。
2.2 启用 Blending
在上述步骤完成后执行项目,会发现 Label 确实染上了 30% 的红色,但下层 PostProcessForDefault
的画面被黑色完全覆盖了:

这是由于启用了后处理的摄像机最终会走到一个名为 post-final
的后处理 Pass,该 Pass 对应的着色器文件内部没有开启 Blending 混合模式,导致在与背景色混合时不会开启 Alpha 通道。
我们可以在资源管理器里搜索 post-final.effect
找到该内置的着色器文件:

打开后加入 blendState
配置,启用透明度混合功能:

保存修改后再执行,CameraForDefault
摄像机所捕获并后处理过的画面(即 PostProcessForDefault
节点),就能被作为背景色混合并展示出来:

💡
post-final.effect
仅会在摄像机开启usePostProcess
时才被使用到,故其修改对整体影响不大(值得修改)。