本章我们尝试着对自定义的 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;
*#inc* *lude <cc-global>*
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* *<cc-global>*
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 的改造,网上也有很多例子,都可以拿来照猫画虎试上一试。
内容参考: