“很多朋友提到后效,就会想起那些 3D 游戏大作,但实际上,后效在 2D 游戏开发中的应用也是非常广泛的。
”
恰当地使用后效,可以使一款 2D 游戏的画质提升好几个台阶。
今天邀请到了社区大佬wing
,给大家分享一下 2D 后效框架相关的技术要点。
什么是后处理效果
“后处理效果简称后效,用于对渲染结果做进一步的处理,以实现许多高级效果和特殊效果。
”
通俗的解释就是对游戏画面进行 PS,在游戏中属于一种不可获缺的功能,常用于提升游戏画质与表现。
以下是一些常用后效:
模糊效果
:可以用于实现景深、运动模糊等效果。辉光效果
:可以增加游戏元素的发光效果,提升游戏的视觉冲击力。色调映射效果
:可以调整游戏的色彩风格,表达不同的情绪和氛围。扭曲效果
:可以创建独特的变形效果,增加游戏的艺术感和创意。颜色校正效果
:可以调整游戏的整体色调和对比度,使画面更加饱满和生动。
2D 后效方案解析
Cocos Creator 目前没有内置支持 2D 阶段的后处理,因此需要大家各显神才能通达到渲染目标。
用得最多的方式就是将相机渲染到一张 RenderTexture
(以下简称 RT) 中,再显示到一个 Sprite
上。
这是一个古老而实用的解决方案,但是这样会对开发流程有一定影响且看起来不那么优雅。
参考社区大佬 @玄烨
和 @二喵
给终极解决方案,我们可以通过代码,将相机渲染的 RT 在处理后直接显示到最终窗口上。由于是通过代码创建一个矩形渲染到最上层,对于使用者来说是无感的,既优雅又方便。
渲染流程如下所示:
这里奉上关键代码:
let device = director.root.device;let renderPipeline = director.root.pipeline;let pso = PipelineStateManager.getOrCreatePipelineState(device, pass, pass.getShaderVariant(), __renderPass, __quad);let cmd = renderPipeline.commandBuffers[0];cmd.beginRenderPass(__renderPass, destination.window.framebuffer, renderArea, __clearColors, 0, 0);cmd.bindDescriptorSet(pipeline.SetIndex.GLOBAL, renderPipeline.descriptorSet);cmd.bindDescriptorSet(pipeline.SetIndex.MATERIAL, pass.descriptorSet);cmd.bindPipelineState(pso);cmd.bindInputAssembler(__quad);cmd.draw(__quad);cmd.endRenderPass();
好啦!大家知道原理了,是不是特别简单,有了核心技术,剩下的就是怎么去组织它,让它用起来更趁手,于是就有了如下几个通用接口 PP_Graphics
:
/*** 通用后处理接口,通过一个材质pass处理一张至目标纹理上*/
blitP(source: RenderTexture, destination: IRenderTexture, pass: renderer.Pass, renderArea: gfx.Rect = null)/*** 拷贝纹理*/
blit(source: RenderTexture, destination: IRenderTexture, renderArea: gfx.Rect = null )/*** 用一种颜色清除纹理*/
clear(destination: IRenderTexture,color: IVec4Like,renderArea: gfx.Rect = null)/*** 翻转纹理*/
flip(source: RenderTexture,destination: IRenderTexture,flipX: boolean,flipY: boolean,renderArea: gfx.Rect = null)
整个后处理渲染框架都是基于以上这几个接口,那么我们继续完善流程,以方便我们直接使用或者扩展更多渲染效果:
其中:
PP_Graphics
包含上述各种基础接口PostProcessingMgr
管理PostProcessing
以及后处理用到的RenderTexture
PostProcessing
绑定在相机上,用于接管相机的渲染结果,并用RT
执行各种效果,并最终输入到屏幕上IPPEffect
是最终的效果处理模块,可以按自己的需求进行排列组合
使用简介
基于以上流程,框架中包含了 10 多种基础效果,使用起来也比较简单,这里以窗口背景模糊为例:
1. 添加 Layer
先添加一个 UI 层,用于分离不同相机渲染的对象, 这里我添加了一个 UI_2D_1 的层级:
2. 创建两个 Canvas
我们需要创建两个 Canvas,并将两个 Canvas 中的节点放于不同的 layer 中:
背景放在 UI_2D_1 层中,用于模糊:
弹窗放入 UI_2D 层中:
3. 修改相机的可见层级
Canvas 下的相机只用渲染所在 Canvas 中的层级即可。
“这里注意下,一般一个流程(2d、3d)的开始只需清屏方式为 DEPTH_ONLY。但是前面如果没有任何相机渲染,这时就需要 SOLID_COLOR 咯;这里相机 1 可见设置为 SolidColor, 相机 2 则为DepthOnly:
”
4. 对相机添加后处理处理
这里只用对背景模糊,即只用处理 camera1 渲染内容,在 camera 上添加组件。
PostProcessing 用于开启后处理 PPE_Blur 用于模糊:
启动预览,可以看到模糊功能就加好啦。
自定义后效
后效的扩展也很方便,只需要如下几个资源、代码:
一个后处理 shader
一个绑定 shader 的材质球
一个后处理扩展 ts 文件,用于给材质球传参
以下用一个屏幕变灰的效果做为例子:
1. 添加一个后处理 shader
precision highp float; #include <pp-shared-ubos>in vec2 v_uv;uniform sampler2D mainTexture;#pragma define intensity matParams.xconst vec3 weight = vec3(0.2126, 0.7152, 0.0722);vec3 Grayscale(in vec3 o, float value){float lumin = dot(o, weight);vec3 final = mix(o, vec3(lumin), value);return final;}vec4 frag() {vec4 pixel = texture(mainTexture, v_uv);pixel.rgb = Grayscale(pixel.rgb, intensity);return pixel;}
2. 添加并编写脚本
编写对应的后处理脚本文件,用于控制材质球参数或者对图形进行多次处理,并暴露相关属性。
在框架中,后处理组件只需要继承 PPE_Base
类即可,一下是图像变灰的后处理组件的实现,找到相关材质球,并在 apply
函数中设置材质的属性即可:
*** 灰度*/
@ccclass('PPE_Grayscale')
export class PPE_Grayscale extends PPE_Base {@property({ range: [0, 1], slide: true, step: 0.01, override: true })intensity: number = 1;get materialPath() {return "materials/pp-grayscale";}public apply(source: RenderTexture, destination: IRenderTexture): void {let pass = this.material.passes[0];PP_Graphics.setUniform(pass, "intensity", this.intensity);PP_Graphics.blitP(source, destination, pass);}
}
3. 创建材质
使用对应的 Shader 创建材质球,放入统一的材质文件夹下,方便后处理框架加载。也可以在编辑器中将材质球拖拽到模块的材质槽中。
4. 调参、预览
在组件中调整好参数后,启动预览,即可获得自己想要的效果。
意外的惊喜(2D 光照效果)
相信不久之前,有人已经看过这张动图了。这是用 Cocos Creator 创建的一个 2D 灯光效果,虽然不是正儿八经的光照系统,但是对于不少项目已经完全够用了,毕竟光嘛,就是图片的局部提亮。
其实早在论坛中就有不少人需要增加 2D 光照,大家给的方案也基本就是后处理中加光照点位。技术不复杂,但是点位的数量或多或少是有限制的,而且调试起来也比较麻烦。
当我将 2D 后处理框架做出来后的一天,我忽然有想到这个问题,既然我的 mask 已经实现,那不就一个妥妥的一个灯光效果嘛,只是数量太少,只支持一个。
那如果我一张图来做为 mask 的遮罩呢?是不是灯光数量就无限了?这和延迟渲染是一个逻辑呀!基于这个想法,当晚就进行了一波测试,事实上这个技术方案更简单,不会儿就验证成功(当然中间有颜色叠加的坑,需要一顿调试),现在流程就变成了:
现在我们就得到一个 Cocos Creator 的 2D 光照系统,我找了张图测试了一番。
嘿,效果还不错,支持无限灯光数量不说,连粒子都可以作为光源,还支持各种异型灯光效果:
一个相机渲染背景
一个相机只渲染灯光,并绑定上后处理器,添加 2D 灯光后处理模块
运行就可以看到效果
结语
大家好,我是 wing,坐标成都,一个 35+ 的老年人。曾经游走于各种引擎之间,目前主用 Cocos。
平时主要工作是解决公司各个项目中的技术难题,也会在业余时间研究和储备各类技术方案,让公司的 Cocos 框架更通用。
这个项目本来也是无心插柳,一开始只是为了解决自己项目中的一个小问题。@二喵
觉得将这个做成一个通用的解决方案,能让我更了解引擎的渲染流程,我就试试做了看。
没想到做了以后发现后处理是一个如此好玩的事情,可以让画面朝自己想要的效果表现,只需要控制各种参数即可。
最终灯光系统也是意外之喜,我获得了一次技能提升,也为社区做了少许贡献,也让需要此功能来表现游戏氛围的开发者和项目有了一条捷径。
感谢大家的阅读,希望能够给有需要的开发者们带来一些帮助!
听懂,掌声!