引子
了解绘制粒子之后,接着去看如何绘制粒子轨迹。
绘制轨迹
在原文中提到绘制轨迹的方法是将粒子绘制到纹理中,然后在下一帧上使用该纹理作为背景(稍微变暗),并每一帧交换输入/目标纹理。这里涉及两个重点使用的 WebGL 功能点:
基于绘制粒子的基础上,增加逻辑的主要思路:
- 初始化时,增加了背景纹理 B 和屏幕纹理 S 。
- 创建每个粒子相关信息的数据时,存了两个纹理 T20 和 T21 中。
- 绘制时,先绘制背景纹理 B ,再根据纹理 T20 绘制所有粒子,接着绘制屏幕纹理 S,之后将屏幕纹理 S 作为下一帧的背景纹理 B 。
- 最后基于纹理 T21 绘制新的结果,生成新的状态纹理覆盖 T20 ,开始下一帧绘制。
不包含随机生成的粒子轨迹效果见示例,下面看看具体的实现。
纹理
新增纹理相关逻辑:
// 代码省略
resize() {
const gl = this.gl;
const emptyPixels = new Uint8Array(gl.canvas.width * gl.canvas.height * 4);
// screen textures to hold the drawn screen for the previous and the current frame
this.backgroundTexture = util.createTexture(gl, gl.NEAREST, emptyPixels, gl.canvas.width, gl.canvas.height);
this.screenTexture = util.createTexture(gl, gl.NEAREST, emptyPixels, gl.canvas.width, gl.canvas.height);
}
// 代码省略
初始化的背景纹理和屏幕纹理都是以 Canvas 的宽高作为标准,同样是以每个像素 4 个分量存储。
屏幕着色器程序
新增屏幕着色器程序对象,最终显示可见的内容就是这个对象负责绘制:
this.screenProgram = webglUtil.createProgram(gl, quadVert, screenFrag);
顶点数据
顶点相关逻辑:
// 代码省略
this.quadBuffer = util.createBuffer(gl, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]));
// 代码省略
util.bindAttribute(gl, this.quadBuffer, program.a_pos, 2);
// 代码省略
gl.drawArrays(gl.TRIANGLES, 0, 6);
// 代码省略
这里可以看出以顶点数据按照二维解析,总共 6 个点,绘制的是一个矩形,为什坐标都是 0 和 1 ,接着看下面的着色器。
顶点着色器
新增顶点着色器和对应绑定的变量:
const quadVert = `
precision mediump float;
attribute vec2 a_pos;
varying vec2 v_tex_pos;
void main() {
v_tex_pos = a_pos;
gl_Position = vec4(1.0 - 2.0 * a_pos, 0, 1);
}
`;
// 代码省略
this.drawTexture(this