形状特效

形状特效

初次接触到形状特效是在剪映,剪映中叫蒙版,就是列举了一些常见形状的蒙层效果,除了常规的旋转、缩放、位移等操作外,还有个虚化的效果。可以去剪映感受下。

接触到这东西,觉得效果挺不错,打算研究下,正好手头的编辑工具项目也可以用上。线性、圆形、矩形,这几个规范的,通过平方根公式 跟 三角形余弦定理基本都可以推算出来(可以参考github shader),重点来了,爱心 & 五角星 怎么搞?



特殊形状

爱心 、五角星,一开始觉得可能是通过 三角函数、关键点、贝塞尔曲线 等数学计算实现的? 想想觉得这样是不是过于复杂, 一翻google之后,还是没啥思路。放大招抓包,剪映的素材都是下发的,必然会有网络请求的,一顿操作之后,如愿拿到了资源包。

爱心

五角星

看到了这两个,很明显不是通过复杂数学计算得到的。 但是这一层一层的渐变红绿图 是什么鬼?
资源包里还有shader文件的😄

float alpha = (col.r * 4.0 * 256.0 + col.g * 255.0) / 781.0;

重点的就是这行计算逻辑,还是懵的。

col.r col.g 对应图上的红绿分量可以理解
*4 /781 是什么鬼? 4 对应 图上的四层?

用颜色取色器取色看看,这一看就明白了。

1
2
3
4
5
6
7
第1层c00d00 
第2层800000~80ff00
第3层400000~40ff00
第4层000000~00ff00

R * 4 + G 算下来 从 0 到 781
781 = 12(c0) * 16 * 4 + 13(0d)

每层红色分量固定(00/40/80/c0),绿色分量渐变0到255(00-ff),所以整个图层解析下来就是 从0 到 781.

alpha 计算下来就有 256 *3 + 1 = 769 个值,最里面的一层最小的,固定为1,保障无虚化的时候也有最基本的形状效果。

搞明白之后,顿时觉得秒啊, 回头一想,为什么要搞的那么多复杂呢? 反正是为了计算alpha,直接搞个灰度图 从黑到白,不就好了?
仔细一想,灰度图一个分量最多256个,最终的效果割裂感会很明显,所以用到了r、g 两个分量,让效果更丝滑,实际上如果你想 也可以用rbg三个分量,重新设计下计算公式 让范围更大更丝滑。

举一反三

学以致用,那必须举一反三下了。

花型

用五角星图去找设计师,参考一下做一个花型的。设计师也一下没掌握精髓,照着搞了一个但不是我想要的。所以有了人生第一次指导设计师画图了,一翻操作之后 搞定了

最后的shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#iUniform float iReverse = 0.

#iUniform float _X = 0.5
#iUniform float _Y = 0.5
#iUniform float _S = 0.25
#iUniform float _A = 0.
#iUniform float _F = 0.00

#iUniform float iPlatform_iOS = 1.0

const float PI = 3.1415926;

#iChannel0 "file://followw813654.jpeg"
#iChannel1 "file://mask1.png"

vec2 rotation(vec2 uv, float angle, float ratio)
{
vec2 center = vec2(0.5, 0.5);
mat2 zRotation = mat2(cos(angle), sin(angle), -sin(angle) * ratio, cos(angle) * ratio);
vec2 centeredPoint = uv - center;
vec2 newUv = zRotation * centeredPoint;
return vec2(newUv.x, newUv.y / ratio) + center;
}


vec2 scale(vec2 uv, vec2 scale)
{
vec2 newPos = vec2(0.5) + (uv - vec2(0.5)) / scale;
return newPos;
}


vec2 offset(vec2 uv, vec2 offset)
{
return uv + offset;
}


void main()
{
highp vec2 textureCoordinate = gl_FragCoord.xy/iResolution.xy;

float radio = iResolution.x / iResolution.y;
bool ls = (radio > 1.0);
vec4 base = texture(iChannel0, textureCoordinate);

float _Radio = 1.0;
float _Scale = _S * 5.;
_Scale = ls ? _Scale / radio : _Scale;
float _Angle = 360. * PI / 180. * _A;
vec2 _Offset = vec2(_X * 2.0 - 1.0, (_Y * 2.0 - 1.0));

vec2 newUV = offset(textureCoordinate, vec2(-_Offset.x, _Offset.y));
newUV = scale(newUV, vec2(1. * _Scale, radio * _Scale));
newUV = rotation(newUV, _Angle, _Radio);
newUV.y = (iPlatform_iOS == 1.) ? newUV.y : (1. - newUV.y);
vec4 mask = texture(iChannel1, newUV) * step(newUV.x, 1.) * step(newUV.y, 1.) * step(0., newUV.x) * step(0., newUV.y);

vec2 col = mask.rg;
// 怎么理解 ? 根据 mask图的 作图颜色搭配 动态调整
// 第1层c00d00 第2层800000~80ff00 第3层400000~40ff00 第4层000000~00ff00
// R * 4 + G 算下来 从 0 到 781
// 781 = 12 * 16 * 4 + 13
float alpha = (col.r * 4.0 * 256.0 + col.g * 255.0) / 781.0;
alpha = smoothstep(0.49 - abs(sin(iTime/2.)), 0.51 + abs(sin(iTime/2.)), alpha);
if (iReverse > .5) {
alpha = 1.0 - alpha;
}
gl_FragColor = mix(vec4(0, 0, 0, alpha), base, alpha);
}

完整效果