前言
最近想实现个酷炫的转场特效,想到了这个烧灼效果。最初想用svg滤镜实现,但动画效果非常卡,遂转而考虑使用webgl的片段着色器实现。下面是最终实现的效果
本文只关注片元着色器,WebGL基础知识可见一下链接:
WebGL 基础概念webglfundamentals.org
画出烧焦的洞
我们使用放射渐变来实现,然后把中间不透明度为1的区域变成完全透明的。
放射渐变的实现非常简单,各像素的颜色仅需要通其到渐变中心的距离确定。
代码
float circle(vec2 st, vec2 center){ return distance(st , center)*3.; } void main(){ vec2 st = gl_FragCoord.xy / u_resolution; float color = circle(st , vec2(.5,.5)); vec4 grandColor = mix(vec4(0. ,0. ,0. ,1.) , vec4(1.,1.,1.,0.) , smoothstep(.58 , .9 , color)); if(grandColor.a == 1.)grandColor.a = 0.; gl_FragColor = grandColor; }
效果
为烧焦的洞添加火焰边缘和熏黄效果
因为基本图形是使用放射渐变制作的,它的不透明度alpha从中间到周围由1渐变为0,所以我们可以利用渐变的值确定各部分位置,比如alpha在0.~.99间是纸张烧焦部分 ,在.99~1.间是火焰的位置,为1.时是中间烧掉的洞。烧焦部分由黑色到黄色的渐变效果,同样可以用alpha值作为度量。
代码
if(grandColor.a == 1.)grandColor.a = 0.; else if(grandColor.a > .994 && grandColor.a <1.){ grandColor = mix( vec4( 0.985,0.0,0.000 ,1.) , vec4(0.985,0.696,0.285 , 1.) , smoothstep(.994, .999 , grandColor.a) ); } else{ grandColor = vec4( mix(vec3(0.,0.,0.) , vec3(0.690,0.373,0.000 ) ,smoothstep(.23 , .55,1.- grandColor.a)) , grandColor.a*1.6); }
制作不规则效果
上面的效果看起来完全不像一个烧焦的洞,它太规则了,为了得到形状更加自然的焦洞,可以使用一种名为柏林噪音(Perlin Noise)的技术。柏林噪音是一种梯度噪音,它的主要原理是在网格的顶点计算出随机的梯度,而后利用插值的方式利用周围顶点的梯度计算各点的噪声值。
这是柏林噪声的实现代码:
vec2 g_random(vec2 ip){ return fract(sin( vec2(dot(ip,vec2(127.1,311.7)), dot(ip,vec2(269.5,183.3))) )* 44753.976967) *2. - 1.; } float g_noise(vec2 st){ vec2 ip = floor(st); vec2 fp = fract(st); vec2 u = fp*fp*fp*(fp*(fp*6.-15.)+10.);//使用三次多项式进行插值 return mix( mix(dot(g_random(ip) , fp-vec2(0.,0.)) , dot(g_random(ip+vec2(1.,0.)) , fp-vec2(1.,0.)) ,u.x), mix(dot(g_random(ip+vec2(0.,1.)) , fp-vec2(0.,1.)) , dot(g_random(ip+vec2(1.,1.)) ,fp-vec2(1.,1.)) , u.x), u.y ); }
在主函数中的相关代码:
vec2 copy = st; copy *= vec2(4.,4.); copy += g_noise(copy*3.); //将放射渐变用噪声扭曲 color += (g_noise(copy) -.5)*.19;
效果
添加纹理
这下看起来好多了,不过还是有些奇怪——熏黄的部分颜色太平滑了,一般的纸张可不会这么光滑,最好再添加一点纹理。这里我们使用简单的噪声来实现
grandColor.a += (random(st*400.)-.5)*.2;
效果
动态效果
动态的实现非常简单,只要在着色器中添加时间的uniform即可。
完整代码:
BokunoMasayume/amazing-animationgithub.com发布于 2019-07-08 WebGL 前端开发 Canvas