Shader 动画是借助于外界传入的实时更新的时间或进度变量来实现基于数学计算的图形变化。我们可以在 ShaderToy 网站中看到类似的预设变量,以满足动画需求:
在 WebGL 中,可以借助 JavaScript 中的定时器 setTimeout
或 requestAnimationFrame
(推荐)来实现定时器循环动画。
一个最简单的循环动画:
(function loop() {
// your code
requestAnimationFrame(loop.bind(this))
})()
在 Three.js 中还可以这么写:
renderer.setAnimationLoop( render );
// Loop
function render(time) {
// your code
}
Shader 的奇妙之处是:以往晦涩难懂的公式,在 Shader 的世界里会变得生动形象起来,若想在一块画布里游刃有余的绘制你想要的图像,则需要在脑海里对这些数学公式有更多感性的认识。
看到 Sin 和 Cos 就能想到连绵起伏的波浪,看到 fract 就能想到鳞次栉比的排列,看到直角坐标系下排列的半圆就能联想到极坐标系下的花朵,看到安静的像素就能想到动态的图形。
这里展示一个更为复杂涉及场景衔接与切换的案例,感受 Shader 动态之美:
See the Pen Shader Animation by ShaderFans (@shaderfans) on CodePen.
Iñigo Quiles 为我们准备了一些常见有用的函数:
还有来自其他人的贡献,这些函数可以使用在图形绘制、动画等方方面面:
在这里你可以查阅到大部分 Shader 的自带函数可视化文档。
一个匀速递增的时间变量自然无法满足所有动画需求,在 CSS 中我们有 ease
、ease-in
、ease-out
等预设缓动函数,也可以使用 cubic-bezier()
函数自行构建贝塞尔曲线。对于第三方的动画库如Tween.js,同样可以使用预设好的缓动算法:
可视化图表如下图所示:
在 Shader 中,我们可以通过一些动画库中写好的数学公式来构建出同样的缓动函数:
或者直接在 Shader 中自动构建出贝塞尔曲线缓动函数并传入数值:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord/iResolution.xy;
float time = fract(iTime*.8);
// KeySpline 为我们构架出来的贝塞尔曲线,后四个值为上图的四个数字:
float line = plot(uv, KeySpline(uv.x, 0.,1.,1.,0.));
float rect = rect(uv+vec2(.5, .5)-vec2(time, KeySpline(time, 0.,1.,1.,0.)), vec2(.01, .01));
vec3 col = vec3(line + rect);
fragColor = vec4(col,1.0);
}
待补充:https://soulwire.co.uk/math-for-motion/
时间变量在转场动画中可以作为切换前后场景的进度条,实现各类转场效果:
See the Pen 1. Image Transition By Shader by ShaderFans (@shaderfans) on CodePen.
对转场感兴趣的同学可以在这个网站中查看更多的案例:
在《图形绘制》这一节中我们提及过通过 SDF 绘制图形的方法,通过定义每个像素距离图形的距离得出了每个点的数值,通过该数值进行着色便可得到对应的图形。正是因为数值的变化是线性的,可以很好的进行差值动画,从而实现出图形之间的形变。
See the Pen Morphing Animation by ShaderFans (@shaderfans) on CodePen.
其中最精华的代码部分就在这里:
// 之所以有形变,正是因为 getShape 得到的图形是渐变的(因为距离是递增或递减的)
// 通过 mix 将两个图形的渐变融合起来,最终使用 smoothedge 将轮廓清晰化,最终就能得到形变过程。
vec3 color = vec3(smoothedge(mix(getShape(st, i0), getShape(st, i1), f)));