Cocos Creator Shader 入门 ⒂ —— 自定义后处理管线

💡 本系列文章收录于个人专栏 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.mtlver-blur.mtlgreen-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 时才被使用到,故其修改对整体影响不大(值得修改)。

相关推荐
用户6737528018842 分钟前
鸿蒙开发:应用内如何做更新
前端
zxhnext23 分钟前
LLM大语言模型入门
前端·后端
知心宝贝30 分钟前
写了那么久的前端,你真的了解浏览器背后的“小动作“吗?
前端·程序员·浏览器
wycode30 分钟前
Vue2实践(2)之用component做一个动态表单(一)
前端·javascript·vue.js
维李设论31 分钟前
前端智能化 | AG-UI实践及原理浅析
前端·aigc·agent
第七种黄昏32 分钟前
Vue3 中的 ref、模板引用和 defineExpose 详解
前端·javascript·vue.js
一只卡比兽33 分钟前
动态规划与贪心算法详解:原理、对比与代码实践
前端
aiwery36 分钟前
一文掌握 TypeScript 工具类型:Record、Partial、Omit、Pick 等实战用法
前端·代码规范
ankleless1 小时前
C语言(12)——进阶函数
前端·html
一条上岸小咸鱼1 小时前
Kotlin 基本数据类型(四):String
android·前端·kotlin