Shader - 波浪加载动效
TIP
使用 shader 实现,波浪加载动效。
看到波浪的表现特点我第一时间想到的就是正弦曲线(或者说是正弦波,又让我想起了示波器)。
# 一. 效果
# 二. 正弦曲线
「正弦曲线」
是三角函数中的一种正弦(Sine)比例的曲线。正弦曲线表现为一条波浪线,形状犹如海上完美的波浪。
标准的正弦函数公式为:
正弦函数属于周期函数,其值域为 [-1, 1]
。
如下图就是一个纯正标准的正弦曲线:
而一般我们常用的正弦曲线公式为:
这条公式比标准公式多了几个常数,含义如下:
A
:「振幅(Amplitude)」
,曲线最高点与最低点的差值,表现为曲线的整体高度ω
:「角速度(Angular Velocity)」
,控制曲线的周期,表现为曲线的紧密程度φ
:「初相(Initial Phase)」
,即当 x = 0 时的相位,表现为曲线在坐标系上的水平位置k
:「偏距(Offset)」
,表现为曲线在坐标系上的垂直位置
# 相位:
相位(Phase):上方公式中的 ωx±φ
部分称为相位,相位发生在周期性的运动之中,最直接的理解就是角度。
有了公式之后,我们可以尝试调整其中的常数来改变函数曲线的形态。
# 1. 改变曲线的高度
我们可以调整常数 A(振幅)
来改变曲线的值域(值域为 [-A, A]
):
# 2. 改变曲线的周期
我们可以调整常数 ω(角速度)
来改变曲线的周期:
# 3. 改变曲线的水平位置
我们可以调整常数 φ(初相)
来改变曲线的水平位置:
其实对于 “曲线的水平位置”
这个描述是不太准确的,因为初相实际上改变的是当 x = 0
时的相位,也就直接影响函数曲线在 x = 0
处的位置。
所以说曲线的位置并没有真正改变,而只是曲线的形态发生了改变。
但是由于正弦曲线的周期性特点,曲线的这种形态变化看起来像是曲线进行了位移。
# 4. 改变曲线的垂直位置
我们可以调整常数 k(偏距)
来改变曲线的垂直位置:
# 三. Effect
shader 代码如下:
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
amplitude: { value: 0.05, range: [0.0, 0.5], editor: { tooltip: '振幅' } }
angularVelocity: { value: 10.0, editor: { tooltip: '角速度' } }
frequency: { value: 10.0, editor: { tooltip: '频率' } }
offset: { value: 0.5, range: [0.0, 1.0], editor: { tooltip: '偏距' } }
toLeft: { value: true, editor: { type: boolean, tooltip: '向左(方向)' } }
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
in vec3 a_position;
in vec4 a_color;
in vec2 a_uv0;
out vec4 v_color;
out vec2 v_uv0;
void main () {
// 使用内置矩阵转换坐标
gl_Position = cc_matViewProj * vec4(a_position, 1);
// 传递顶点颜色
v_color = a_color;
// 传递 UV 坐标
v_uv0 = a_uv0;
}
}%
CCProgram fs %{
precision highp float;
// 引入 Cocos Creator 内置的全部变量
#include <cc-global>
in vec4 v_color; // 顶点颜色
in vec2 v_uv0; // UV 坐标
uniform sampler2D texture; // 纹理
// 自定义属性
uniform Properties {
float amplitude; // 振幅
float angularVelocity; // 角速度
float frequency; // 频率
float offset; // 偏距
bool toLeft; // 是否向左
};
void main () {
// 保存顶点颜色
vec4 color = v_color;
// 叠加纹理颜色
color *= texture(texture, v_uv0);
// 直接丢弃原本就透明的像素
if(color.a == 0.0) discard;
// 初相位(正值表现为向左移动,负值则表现为向右移动)
// cc.time 是 Cocos Creator 引擎提供的运行时间全局变量(类型:vec4)
// float initiaPhase = frequency * cc_time.x;
// 方向(左正右负)
// float direction = toLeft ? 1 : -1;
// 代入正弦曲线公式计算 y 值
// y = Asin(ωx ± φt) + k
float y = amplitude * sin((angularVelocity * v_uv0.x) + ((frequency * cc_time.x) * (toLeft ? 1. : -1.))) + offset;
// 丢弃 y 值以上的像素(左上角为原点 [0.0, 0.0])
if(v_uv0.y < y) discard;
// 输出颜色
gl_FragColor = color;
}
}%