<十六>入门CocosShader之实现贴图溶解效果
前言
如果想实现一个溶解效果,就需要标记哪些像素要显示,哪些像素要消失。比较好的办法是用一张噪声图来给每一个像素做标记,噪声图类似下图。由于噪声图只有黑白灰三种颜色,黑白灰非均匀分布,因此,可以利用黑透、白不透、灰半透的原理实现不规则溶解效果。
溶解效果最简单的制作通常需要两个参数:一个是噪声图,一个是控制过滤黑/白像素的阈值。
基础了解:


实现溶解效果
接上一篇中实现的sprite shader来实现溶解效果
- 添加实现溶解效果需要的两个参数:噪声图 和 控制过滤参数
- 处理溶解逻辑
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- passes:
- vert: sprite-vs:vert
frag: sprite-fs:frag
depthStencilState:
depthTest: false
depthWrite: false
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
alphaThreshold: { value: 0.5 } # 定义属性透明度阈值,在启用透明测试的时候,透明度低于阈值内值的片段会被丢弃
# 这里对应的uniform是在sprite-fs的alpha-test中定义的
u_dissolveMap: { value: white, editor: { tooltip: '噪声贴图' } }
dissolveThreshold: { value: 0.5, editor: { range:[0, 1, 0.01], slide: true, tooltip: '溶解阈值' } }
}%
CCProgram sprite-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#if USE_LOCAL
#include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
#include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;
out vec4 color;
out vec2 uv0;
vec4 vert () {
vec4 pos = vec4(a_position, 1);
//因为2D大部分图像都是用来做UI的,UI的变化频率很大
//假设所有的UI都是动态的,因此需要每帧检查对象是否需要重新计算
//这时就会在CPU进行模型空间到世界空间到转换,但这种做法不一定适合所有的项目
//可能有些项目的UI比较固定变化不大,顶点数据直接提供模型空间即可,后续的坐标转换都在GPU上进行,可以减少CPU的开销
//因此提供了USE_LOCAL这样一个宏定义,让用户自己决定2D顶点数据的计算方式
#if USE_LOCAL
pos = cc_matWorld * pos;
#endif
//这里的宏是处理像素对齐的,比如像素风游戏每个像素的边缘都很是清晰分明的,在进行多次坐标转换时精度差值也会变大
//可能会出现像素抖动的现象
//这里的像素对齐就是对观察空间的像素做取整处理
//然后再让它转换到裁剪空间,可以有效避免像素抖动
#if USE_PIXEL_ALIGNMENT
pos = cc_matView * pos;
pos.xyz = floor(pos.xyz);
pos = cc_matProj * pos;
#else
pos = cc_matViewProj * pos;
#endif
uv0 = a_texCoord;
#if SAMPLE_FROM_RT
CC_HANDLE_RT_SAMPLE_FLIP(uv0);
#endif
color = a_color;
return pos;
}
}%
CCProgram sprite-fs %{
precision highp float;
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>
in vec4 color;
#if USE_TEXTURE
in vec2 uv0;
//顶点属性的内存分配
#pragma builtin(local)//申请local内存
layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;//layout代表绑定到local内存指定位置
uniform sampler2D u_dissolveMap;// 熔岩形状的纹理;
#endif
//定义溶解阈值,注意在CocosCreator中所有的非sample的unifrom都应以block的形式声明,否则会报错
uniform Dissolve{
float dissolveThreshold;// 熔岩阀值[0, 1];
};
vec4 frag () {
vec4 o = vec4(1, 1, 1, 1);
//在主贴图之前处理溶解逻辑
float value = 1.0;
#if USE_TEXTURE
vec4 dissolveMap = texture(u_dissolveMap, uv0); // 如果颜色的 r 分量小于阀值,将这个着色操作丢弃;
value *= dissolveMap.r;//选取rgb任意一个通道
#endif
if (value < dissolveThreshold) {
discard; // 将小于阈值的片段丢弃,形成溶解
}
#if USE_TEXTURE
//o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
o *= texture(cc_spriteTexture,uv0);//直接做纹理处理即可
#if IS_GRAY //引擎设置的灰度计算
float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
o.r = o.g = o.b = gray;
#endif
#endif
o *= color;
if (value < dissolveThreshold + 0.05) {
o = vec4(0.9, 0.6, 0.3, o.a); // 溶解的边缘设置一个边缘过度色,让溶解效果更平滑一些
}
ALPHA_TEST(o);//在启用alpha测试时,会调用,如果小于阈值会被丢弃,以达到该区域呈现透明的效果
return o;
}
}%
3.测试运行

- 代码控制动态消融效果
import { _decorator, Component, EffectAsset, Graphics, Material, Node, Sprite } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Draw')
export class Draw extends Component {
// @property(EffectAsset)
// effect: EffectAsset = null;//声明一个属性去关联EffectAsset
_value = 0
start() {
// //获取基础绘制组件Graphics
// const g = this.getComponent(Graphics);
// //绘制并填充矩形
// g.fillRect(0, 0, 400, 300);
// //new一个材质
// const mat = new Material();
// //初始化
// mat.initialize({ effectAsset: this.effect ,defines: {USE_TEXTURE:true}});
// //添加材质到sprite
// const spComp = this.node.getComponent(Sprite);
// spComp.customMaterial = mat;
this.node.once(Node.EventType.TOUCH_START, this.disApear,this);
}
disApear(){
if(this._value >= 1)return;
this._value += 0.01;
const sprite = this.getComponent(Sprite);
const mat = sprite.customMaterial;
mat.setProperty('dissolveThreshold', this._value);
this.scheduleOnce(()=>{
this.disApear();
},0.01);
}
}
浙公网安备 33010602011771号