来点新花样!使用噪声图实现不规则溶解效果
在上一章里,我们成功地利用了自定义 Effect 和材质,替换了 Sprite 组件内置的材质。本章我们尝试着对自定义的 Effect 做一些改造来实现贴图溶解效果。
为了实现这种效果,需要标记出哪些像素显示、哪些像素消失,比较好的办法是用一张噪声图来给每一个像素做标记,噪声图类似下图。由于噪声图只有黑白灰三种颜色,黑白灰非均匀分布,因此,可以利用黑透、白不透、灰半透的原理实现不规则溶解效果。
溶解效果最简单的制作通常需要两个参数:一个是噪声图,一个是控制过滤黑/白像素的阈值。接下来,尝试定义这两个参数。
Cocos Effect
CCEffect
在之前的文章里我们知道, CCEffect 包裹的是用 YAML 格式编辑的渲染流程描述清单,主要内容涉及与编辑器的交互以及与 CCProgram 的数据交互。CCEffect 的结构再次回顾一下,内容大致如下:
CCEffect %{
techniques: # technique 渲染技术代表完成一个最终效果的方案。一个方案可以由一个或者多个 Pass 融合完成。
- passes: # 一个 Pass 就是一次 GPU 绘制,一般包括一次顶点着色器和片元着色器。
- vert: vs:vert
frag: fs:frag
}
CCProgram vs %{
//...
}
CCProgram fs %{
//...
}
这是一个最最最基础的 shader 声明(例如:绘图组件 Graphics 的 shader),如果要加入一个参数或者贴图,就无法满足。所以我们需要了解 pass 更多的参数。最常用的几个参数如下:
pass 参数:
其中,blendState,depthStencilState,rasterizerState 了解即可。
properties 参数:
defalut values:
更多 pass 和 properties 参数可以参考下方参考文献里的“pass 可选配置参数”。
了解了一些重要的参数后,就可以开始定义参数了,在上一章最终修改后的 shader 上加入一些内容:
CCEffect %{
techniques:
- passes:
- vert: vs:vert
frag: fs:frag
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
u_dissolveMap: { value: white, editor: { tooltip: '噪声贴图' } }
dissolveThreshold: { value: 0.5, editor: { range:[0, 1, 0.01], slide: true, tooltip: '溶解阈值' } } # 此处定义的参数都必须指向 CCProgram 处对应声明的 uniform
}%
CCProgram vs %{
precision highp float;
#include
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);
pos = cc_matViewProj * pos;
uv0 = a_texCoord;
color = a_color;
return pos;
}
}%
CCProgram fs %{
precision highp float;
in vec4 color;
uniform Dissolve{
float dissolveThreshold;// 熔岩阀值[0, 1];
};
#if USE_TEXTURE
in vec2 uv0;
uniform sampler2D u_dissolveMap;// 熔岩形状的纹理;
#pragma builtin(local)
layout(set = 2, binding = 10) uniform sampler2D cc_spriteTexture;
#endif
vec4 frag () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
o *= texture(cc_spriteTexture, uv0);
#endif
o *= color;
return o;
}
}%
接着,回到编辑器,选择材质,就可以看到新增了两个可调整参数:
关联噪声后,根据溶解阈值,处理溶解度:
CCEffect %{
techniques:
- passes:
- vert: vs:vert
frag: fs:frag
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
u_dissolveMap: { value: white, editor: { tooltip: '噪声贴图' } }
dissolveThreshold: { value: 0.5, editor: { range:[0, 1, 0.01], slide: true, tooltip: '溶解阈值' } } # 此处定义的参数都必须指向 CCProgram 处对应声明的 uniform
}%
CCProgram vs %{
precision highp float;
#include
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);
pos = cc_matViewProj * pos;
uv0 = a_texCoord;
color = a_color;
return pos;
}
}%
CCProgram fs %{
precision highp float;
in vec4 color;
uniform Dissolve{
float dissolveThreshold;// 熔岩阀值[0, 1];
};
#if USE_TEXTURE
in vec2 uv0;
uniform sampler2D u_dissolveMap;// 熔岩形状的纹理;
#pragma builtin(local)
layout(set = 2, binding = 10) uniform sampler2D cc_spriteTexture;
#endif
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;
#endif
if (value < dissolveThreshold) {
discard; // 将小于阈值的片段丢弃,形成溶解
}
#if USE_TEXTURE
o *= texture(cc_spriteTexture, uv0); // 与原纹理混合;
#endif
o *= color;
if (value < dissolveThreshold + 0.05) {
o = vec4(0.9, 0.6, 0.3, o.a); // 溶解的边缘设置一个边缘过度色
}
return o;
}
}%
最后,我将原图替换上 Cocos logo,将阈值调整为 0.2,画面呈现的效果如下:
如果想要在运行时修改阈值,可以采用如下方法:
const sprite = this.getComponent(Sprite);
const mat = sprite.customMaterial;
mat.setProperty('dissolveThreshold', 0.5);
到这里为止,我们就完成了一个 shader 的改造,看起来是不是很简单。入门 shader 需要我们不断去尝试,所以大家在学习的时候一定要多去尝试一些 shader 的改造,网上也有很多例子,都可以拿来照猫画虎试上一试。
内容参考:
pass 可选配置参数:
https://docs.cocos.com/creator/3.2/manual/zh/material-system/pass-parameter-list.html
Cocos Shader 基础入门系列
···更新中···
该系列教程视频版
已更新至 EP07
欢迎点击【阅读原文】
前往 B 站观看交流 ↓
B 站关注「Cocos 引擎官方」
https://www.bilibili.com/video/BV1Cq4y1d726