对实现动画的前端同学们来说,canvas
能够说是最自在,最能全面管制的一个动画实现载体。岂但能通过 javascript
控制点、线、面的绘制,应用图片资源填充;还能扭转输出参数作出交互动画,齐全管制动画过程中的动作轨迹、速度、弹性等因素。
但应用 canvas
开发过较简单一点的动画的同学,可能会发现,齐全应用 javascript
绘制、管制的动画,某些成果不太好实现(这篇文章只探讨 2D),像含糊,光照,水滴等成果。尽管用逐像素解决的办法也能够实现,但 javascript
对这类型大量数据的计算并不善于,实现进去每一帧绘制的工夫非常感人,用他实现动画并不事实。
但 canvas
除了最罕用的javascript
API 绘制形式(getContext('2d')
),还有 WebGL 的形式(getContext(webgl)
),对后面说到的大量数据计算的场景,能够说是最适宜施展的中央。WebGL 对很多同学来说就是实现 3D 场景的,其实对 2D 绘图来说,也有很大的施展场景。
为什么 WebGL 会比拟厉害
咱们来看看javascript
API 绘制和 webGL 绘制原理上的不同之处:
如果应用 javascript
对画布的一一像素进行解决,那这部分解决工作就须要在 javascript
的运行环境里进行,咱们晓得 javascript
的执行是单线程的,所以只能一一一一像素进行计算和绘制。就像一个修长的漏斗,一滴一滴水的往下漏。
而 WebGL 的解决形式,是用 GPU 驱动的,对每一个像素的解决,都是在 GPU 上执行,而 GPU 有许多渲染管道,这些解决能够在这些管道中并行执行,这就是 WebGL 善于这种大量数据计算场景的起因。
WebGL 那么厉害,都用它绘图就好喇
WebGL 尽管有下面说的长处,但也有个致命的毛病:不好学,想要简略画根线也要费一番力量。
GPU 并行管道之间是不晓得另一个管道输入的是什么,只晓得本人管道的输出和须要执行的程序;而且不保留状态,管道本人并不知道在这次工作之前执行过什么程序,有什么输入输出值,相似当初纯函数的概念。这些观点上的不同就晋升了应用 WebGL 绘图的门槛。
另外这些跑在 GPU 里的程序不是javascript
,是一品种 C 语言,这也须要前端同学们另外再学习。
Hello, world
那门槛再高也总有须要跨过来的一天的,上面一步一步管制 WebGL 去 画
一点图案,大家也能够领会一下,适宜在什么时候应用这一门技术。
根底环境——大荧幕
为尽快进入 GLSL 着色器的阶段,这里根底 WebGL 环境搭建用了Three.js
,大家能够钻研下这个根底环境的搭建,不必第三方库其实也用不了多少代码量。
以下是根底环境的搭建:
function init(canvas) {const renderer = new THREE.WebGLRenderer({canvas});
renderer.autoClearColor = false;
const camera = new THREE.OrthographicCamera(
-1, // left
1, // right
1, // top
-1, // bottom
-1, // near,
1, // far
);
const scene = new THREE.Scene();
const plane = new THREE.PlaneGeometry(2, 2);
const fragmentShader = '............'
const uniforms = {u_resolution: { value: new THREE.Vector2(canvas.width, canvas.height) },
u_time: {value: 0}
};
const material = new THREE.ShaderMaterial({
fragmentShader,
uniforms,
});
scene.add(new THREE.Mesh(plane, material));
function render() {
material.uniforms.u_time.value++;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render()}
解释一下下面这段代码做了什么:创立了一个 3D 场景(说好的 2D 呢?),把一个矩形立体糊在摄像机后面,占满摄像机视觉范畴,就像看 IMAX 坐最前排,你能看到的就只有背后的屏幕的感觉,屏幕上的画面就是你的整个世界。咱们的绘图就在这个屏幕上。
再阐明一下,着色器分为顶点着色器 VERTEX_SHADER
和片段着色器FRAGMENT_SHADER
。
顶点着色器对 3D 场景里物体的每个顶点计算值,如色彩、法线向量等,在这里咱们只探讨 2D 画面,顶点着色器的局部就由 Three.js
代劳了,实现的作用就是固定了场景中镜头和屏幕的地位。
而片段着色器的作用就是计算立体上每一个片段(在这里是屏幕上每一个像素)输入的色彩值,也是这篇文章钻研的对象。
片段着色器入参有 varying
和uniform
两种,varying
简略说一下是由顶点着色器传入的,每个片段输出的值由相干的顶点线性插值失去,所以每个片段上的值不一样,本文先不探讨这部分(不然写不完了)。uniform
是对立值,由着色器内部传入,每个片段失去的值是一样的,在这里就是咱们从 javascript
输出变量的入口。下面的代码咱们就为片段着色器传入了u_resolution
,蕴含画布的宽高值。
第一个着色器
fragmentShader
为着色器的程序代码,个别的形成为:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
void main() {gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
在前 3 行查看了是否定义了 GL_ES
,这通常在挪动端或浏览器下会定义,第 2 行指定了浮点数float
的精度为中等,也能够指定为低精度 lowp
或高精度highp
,精度越低执行速度越快,但品质会升高。值得一提的是,同样的设置在不同的执行环境下可能会体现不一样,例如某些挪动端的浏览器环境,须要指定为高精度能力取得和 PC 端浏览器里中等精度一样的体现。
第 5 行指定了着色器能够接管哪些入参,这里就只有一个入参:类型为 vec2 的u_resolution
。
最初 3 行形容了着色器的主程序,其中能够对入参和其余信息作解决,最初输入色彩到gl_FragColor
,代表这个片段显示的色彩,其中 4 个数值代表RGBA
(红、绿、蓝、透明度),数值范畴为0.0 ~ 1.0
。
为什么要写 0.0
而不是 0
呢,因为 GLSL
里不像 javascript
数字只有一个类型,而是分成整形 (int
) 和浮点数 (float
),而浮点数必须蕴含小数点,当小数点前是 0 的时候,写成.0
也能够。
那大家看完这段讲解,应该能猜到下面的着色器会输入什么吧,对,就是全屏的红色。
这就是最根底的片段着色器。
应用 uniform
大家应该留神到下面的例子没有用到传入的 uniform 值,上面来说一下这些值怎么用。
看之前搭建根底环境的 javascript
代码能够看到,u_resolution
存储了画布的宽高,这个值在着色器有什么用呢?
这要说到片元着色器的另一个内建的值 gl_FragCoord
,这个值存储的是片段(像素)的座标x
,y
值,应用这两个值就能够晓得以后着色器计算的是画布上哪个地位的色彩。举个例子:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
gl_FragColor = vec4(st, 0.0, 1.0);
}
能够看到这样的图像:
下面的着色器代码,应用归一化后的 x
、y
座标输入到 gl_FragColor
的红、绿色局部。
从图中能够看出,gl_FragCoord
的 (0, 0)
点在左下角,x 轴和 y 轴方向别离为向右和向上。
另一个 uniform 值 u_time
就是一个随着工夫一直减少的值,利用这个值能够使图像随工夫变动,实现动画的成果。
下面的着色器再改写一下:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
gl_FragColor = vec4(st, sin(u_time / 100.0), 1.0);
}
能够看到下图的成果:
着色器中应用三角函数sin
,在色彩输入的蓝色通道做一个从 0 到 1 的周期变动。
还能做什么?
把握根本的原理后,就是开始从巨匠的作品中学习了。shadertoy 是一个相似 codepen 的着色器 playgroud,下面的着色器都是利用下面的根本工具,还有一些造型函数,造出各种目迷五色的特效、动画。
下面就是 GLSL 着色器根本的开发工具,当初就能够开始开发你本人的着色器,剩下就是应用数学方面的技能了。
欢送关注凹凸实验室博客:aotu.io
或者关注凹凸实验室公众号(AOTULabs),不定时推送文章。