Cocos 2.3 上边对于Shader 的编写使用终于 正式化了. 写几个示例供大家参考:
Shader 最新格式:
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
CCEffect %{
// teq
}%
CCProgram vs %{
void main () {
}
}%
CCProgram fs %{
void main () {
}
}%
格式说明:
- CCEffect %{}% 中 编写techniques 相关声明, 与旧版相比, 采用了yaml 编写格式, 而旧版则用json格式. yaml 标准格式目前已有很多地方可见其身影,如 springboot 项目中的配置文件. 优点是语法精简, 且能够包含继承关系等配置.
- CCProgram 则用来声明渲染管线的代码片断, 其中需要包含代码片断的入口函数void main() 与旧版基本没有太大差别,其中变量传递废弃了标准glsl 语法中的 attribute/ varying 限定符, 而统一改用 in/out/ inout 关键字.
编写示例:
- CCEffect格式:
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties: // 声明自定义的外部控制属性, 用于在材质系统面板上或动态运行时进行参数控制修改. 对应uniform的声明
texture: { value: white }
alphaThreshold: { value: 0.5 }
time: { value: 0.0 }
strength: {value: 2.0 }
width: { value: 0.008 }
reverse: { value: 0 }
}%
- CCProgram vs 片断:
vs 片断,无特殊需求的话,基本不需要改动, 即顶点不做额外控制, 进行正常的映射变换后传递给gl_Position即可, 示例中将不包含vs片断代码.
CCProgram vs %{
precision highp float;
#include <cc-global> // 需要显式的包含内置变量声明文件,如cc_matViewProj 等内置对象.
#include <cc-local>
in vec3 a_position; // 相当于旧版的attribute 声明, 用于从渲染管线中获取当前属性信息.
in vec4 a_color;
out vec4 v_color; // 相当于旧版的 varying 声明, 用于向下一个渲染管道传递数据.
#if USE_TEXTURE
in vec2 a_uv0;
out vec2 v_uv0;
#endif
void main () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
v_uv0 = a_uv0;
#endif
v_color = a_color;
gl_Position = pos;
}
}%
- CCProgram fs 片断:
多数shader 功能实现仅是对 像素颜色进行若干控制/变换, 因此shader中的多数逻辑都在于控制某个像素颜色的显示.
CCProgram fs %{
precision highp float;
#include <alpha-test>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
void main () {
vec4 o = vec4(1, 1, 1, 1);
o *= texture(texture, v_uv0);
o *= v_color;
gl_FragColor = o;
}
}%
以上 fs代码片断,没有任何功能, 仅对贴图(SpriteFrame)各点进行采样, 然后与当前节点(Node)的颜色v_color 进行混合并输出. 即实现了系统默认的 buildin-sprite 材质效果.
下面提供若干效果样例, 以实现常见的shader效果.
- 毛玻璃效果(模糊效果)
高斯模糊效果的原理,是将贴图上相邻的若干像素颜色进行加权平均, 即让周围像素的颜色对某一像素产生影响,你中有我,我中有你.
CCProgram fs %{
// 省略若干声明
void main () {
vec4 o = vec4(1, 1, 1, 1);
// 随机采样次数.
float repeats = 5.0;
for(float i=0.0; i<repeats; i++) {
// 以下两步, 主要用来产生一个随机偏移量, 即以当前v_uv0坐标为基础, 叠加一个偏移量, 从而获得偏移后的周边某点的采样颜色.
vec2 q = vec2(
cos(degrees(i*360.0/repeats)),
sin(degrees(i*360.0/repeats))
);
q*= (rand(vec2(i,v_uv0.x + v_uv0.y ))+ num );
vec2 uv2 = v_uv0 + q*num;
// 将周边某点颜色叠加到一起进行颜色混合.
o += texture(texture, uv2);
}
// 中和, 刚才的循环累加了repeats 次, 颜色分量应当除以repeats, 否则颜色分量可能超过1,即高曝光效果.
o /= repeats;
// 降低亮度. 并用节点本身颜色进行混合.
float light = 0.5;
o = o* v_color* light;
o.a = 1.0;
gl_FragColor = o;
}
}%
效果:
- 圆形头像裁剪效果(还能带羽化)
圆形头像裁剪的原理就是以 贴图中心点为基准,画一个半径0.5r 的圆形, 距离中心点超过0.5r 的坐标点的颜色统统 discard掉.
CCProgram fs %{
precision highp float;
#include <alpha-test>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
uniform Params{ // 与旧版的又一声明变化, 旧版 直接声明 uniform vec2 center; 而新版则要求,所有的uniform 声明放到一个UBO {}中, "Parms" 名称随意, 其中的声明字段需要进行字节对齐.详细看官方文档.
vec2 center;
float radius;
float feather;
};
void main () {
// 计算当前坐标点与 中心点(0.5, 0.5)的距离, 距离超过半径的点,直接 discard; 即return.
float dis = distance(v_uv0, center);
if( dis > radius ){
discard;
}
// 正常计算坐标点的颜色值.
vec4 o = vec4(1, 1, 1, 1);
o *= texture(texture, v_uv0);
o *= v_color;
// 判断圆形周边的一圈像素, 根据羽化参数大小, 对周边一圈颜色的透明度进行平滑降低, 即>0.5r 为透明a=0, < 0.4r为不透明a=1, 否则透明度a= 线性0~1过渡.
// dis < 0.4 则为1, >0.5则为0, 否则就为0~1之间插值
if( feather > 0.0 ){
o.a = smoothstep(radius, radius - feather, dis);
}
gl_FragColor = o;
}
}%
效果:
- 溶解效果(若干像素变没了, 像腐烂的破布)
CCProgram fs %{
precision lowp float;
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
uniform Timer{
float time;
};
void main () {
vec4 o = v_color * texture2D(texture, v_uv0);
// 将当前坐标点的三个颜色分量进行混合, 并把其中小于某值的颜色 discard;掉.
// 实际应用场景可能需要另外单独提供一张用来判断是否discard像素的贴图,此处简化,直接对当前图像的色彩进行判断.
float h = (o.g + o.r +o.b)/3.0;
// time 参数为uniform, 即通过运行时代码传递进来的参数.动态修改time 即颜色动态融化的效果.
if(h < time) {
discard;
}
gl_FragColor = o;
}
}%
静态效果:
附时间time 更新组件, 直接挂在相应的sprite节点上即可.
const { ccclass, property } = cc._decorator;
@ccclass
export default class ShaderTime extends cc.Component {
@property
max: number = 0.0;
@property
step: number = 0.001
@property
loop: number = 0;
private mat: cc.Material = null;
private current: number = 0;
private finished: boolean = false;
private loopTimes: number = 0;
onEnable() {
const sp = this.getComponent(cc.Sprite);
if (!sp) {
return;
}
const mat = sp.getMaterial(0);
mat.setProperty("time", this.max);
this.mat = mat;
this.finished = false;
this.loopTimes = this.loop <= 0 ? 2 ^ 63 : this.loop;
}
update(dt) {
if (this.finished) return
this.current += this.step;
if (this.current >= this.max) {
this.current = 0.0;
if (--this.loopTimes <= 0) {
this.finished = true;
return
}
}
if (this.mat) {
this.mat.setProperty("time", this.current);
}
}
}
– 圆角矩形(切圆角必备, 但不完美)
圆角矩形就是将大于圆角半径的若干像素discard,保留其他像素.
CCProgram fs %{
precision highp float;
#include <alpha-test>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
uniform Params{
float radius;
};
void main () {
vec4 o = vec4(1, 1, 1, 1);
o *= texture(texture, v_uv0) * v_color;
// 计算出以4个圆角圆心的坐标点区域.
vec2 uv = v_uv0 - vec2(0.5,0.5);
uv.x = abs(uv.x);
uv.y = abs(uv.y);
float r = 0.5- radius;
if (uv.x > r && uv.y > r) {
uv.x -= r;
uv.y -= r;
// 计算平移后的坐标点距离圆心(0 ,0 )的距离, 与圆形头像原理一致,
float dis = distance(uv, vec2(0,0));
if( dis >= radius ) {
discard;
}
}
gl_FragColor = o;
}
}%
效果:
- 扫光效果 ( 柯南眼镜上总会闪现一道光,秘密被揭开的感觉)
扫光效果的原理就是 将一块区域内的像素颜色增强,使其变的更亮,甚至曝光变白的效果, 再配合时间进行区域平移即可.
CCProgram fs %{
// 省略若干
void main () {
float start = time;
vec4 color = v_color * texture(texture,v_uv0);
// 斜线控制区域. 其实就是 y = -x+width y = -x -width 夹着的区域. 还可以乘上斜率,以使扫光的倾斜程度变化. 加上time 时间控制, 可以使区域根据时间推移,平滑的从左往右移动.
if(v_uv0.x <= (-v_uv0.y + width + start) && v_uv0.x >= (-v_uv0.y - width + start)) {
color *= strength; // 给颜色增强若干倍.
}
// else if( reverse != 0 ) {
// // 可以通过参数控制, 是否仅显示扫光区域, 看起来就会变成一道光照亮了一片区域,而其他地方则是虚无. 手电筒的效果.
// discard;
// }
gl_FragColor = color;
}
}%
效果:
- 其他色彩效果 ( 涉及PS 对颜色的各种处理算法.)
// 冰冻效果
gl_FragColor.r = abs(col.r-col.g-col.b)*3.0/2.0;
gl_FragColor.g = abs(col.g-col.b-col.r)*3.0/2.0;
gl_FragColor.b = abs(col.b-col.r-col.g)*3.0/2.0;
gl_FragColor.a = 1.0;
// 熔铸效果类似打铁的场景
gl_FragColor.r = col.r*0.5/(col.g+col.b);
gl_FragColor.g = col.g*0.5/(col.r+col.b);
gl_FragColor.b = col.b*0.5/(col.r+col.g);
gl_FragColor.a = 1.0;
// 颜色加变暗效果
gl_FragColor.r = col.r*col.r;
gl_FragColor.g = col.g*col.g;
gl_FragColor.b = col.b*col.b;
gl_FragColor.a = 1.0;
// 反相效果
gl_FragColor.r = col.g*col.b;
gl_FragColor.g = col.r*col.b;
gl_FragColor.b = col.r*col.g;
gl_FragColor.a = 1.0;
// 颜色饱和度增强
float vibrance = 4.0;
float average = (col.r + col.g + col.b) / 3.0;
float mx = max(col.r, max(col.g, col.b));
float amt = (mx - average) * (-vibrance * 3.0);
col.rgb = mix(col.rgb, vec3(mx), amt);
gl_FragColor = col;
各自效果:
PS中各常见算法引用:
https://blog.csdn.net/kezunhai/article/details/41832317