对实现动画的前端同学们来说,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_ESprecision mediump float;
No comments:
Post a Comment