前面介绍了如何绘制单个点,本章主要介绍如何绘制多个点,绘制多个点依旧以绘制二维图像为例来讲解,因为绘制二维通向与绘制三维图像一样。
之前的方式可以通过循环来绘制多个点,一次需要绘制多个点,需要同时传递进去多个点的数据。刚好,在WebGL中提供了一种机制:缓存区对象(buffer data),缓存区对象可以同时向着色器传递多个顶点坐标。缓存区是WebGL中的一块内存区域,我们可以向里面存放大量顶点坐标数据,可随时供着色器使用。
使用缓存区步骤
- 创建缓存区对象(gl.createBuffer())
- 绑定缓存区对象(gl.bindBuffer())
- 将数据写入缓存区对象(gl.bufferData())
- 将缓存区对象分配给一个attribute变量(gl.vertexAttribPointer())
- 开启attribute变量(gl.enableVertexAttribArray())
具体创建过程
首先,我们仍然需要创建WebGL对象、片元着色器以及顶点着色器,具体创建的步骤以及原理,可参考之前的教程。具体代码实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42<!-- 书写着色器代码 -->
var v_Shader = `
attribute vec4 a_p;
void main() {
gl_Position = a_p;
gl_PointSize = 10.0;
}
`;
var f_Shader = `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
<!-- 创建WebGL -->
var canvas = document.getElementById(id);
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
<!-- 创建片元操作程序 -->
var program = gl.createProgram();
<!-- 创建片元着色器 -->
var fsShader = gl.createShader(gl.FRAGMENT_SHADER);
<!-- 着色器对象绑定着色器代码 -->
gl.shaderSource(fsShader, f_Shader);
<!-- GLSE语法编译成js语法 -->
gl.compileShader(fsShader);
<!-- 创建顶点着色器 -->
var vsShader = gl.createShader(gl.VERTEX_SHADER);
<!-- 着色器对象绑定着色器代码 -->
gl.shaderSource(vsShader, v_Shader);
<!-- GLSE语法编译成js语法 -->
gl.compileShader(vsShader);
<!-- 运行程序绑定着色器-->
gl.attachShader(program, vsShader);
gl.attachShader(program, fsShader);
<!-- WebGL与program建立连接 -->
gl.linkProgram(program);
<!-- 使用此program -->
gl.useProgram(program);
<!-- 初始化WebGL已经完成 -->
当创建好WebGL之后,可以通过着色器中的attrbute或者uniform对象来传递需要动态修改或设置的的变量。
接下来我们需要进行缓冲区的操作:
首先,需要创建一个缓冲区来承载大量顶点的坐标
(代码继续上文)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- 创建缓存区 -->
var vertexBuffer = gl.createBuffer();
if(!vertexBuffer) {
log('创建缓存区失败。');
return -1;
}
<!-- 将创建的缓存区对象绑定到target表示的目标上 -->
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
<!-- 开辟存储空间,向绑定在target上的缓存区对象中写入数据 -->
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
<!-- 获取着色器中的变量值 -->
var a_position = gl.getAttribLocation(gl.program, 'a_p');
<!-- 将缓存区对象绑定到着色器变量中 -->
gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 0, 0);
<!-- 启用缓存区 -->
gl.enableVertexAttribArray(a_position);
<!-- 绘制缓存区中画的多个顶点 -->
gl.drawArrays(gl.POINTS, 0 , array);
看完了绘制过程,让我们来拆解一下具体内容:
首先,我们要在茫茫内存中申请一个区域来放置缓存区对象的内容,但是我们无法直接放置缓存对象进入内存中,否则会无法识别对应的数据类型,从而无法达到存取自如的境界,那我们就需要将数据的类型告知内存,bingBuffer就是为解决此问题诞生的,函数会在内存中申请一部分区域,并且通过target来制定数据类型,也就是说,缓存区是需要放置在target表示的类型部分去存储。
gl.bindBuffer(target, buffer)
target: 指定存储缓存区的目标类型
- gl.ARRAY_BUFFER : 指缓存区中包含了顶点的数据
- gl.ELEMENT_ARRAY_BUFFER : 指缓存区中包含了顶点数据的索引值
buffer: 自己创建的缓存区对象
接下来,我们需要做的是填充刚刚申请的缓存区,我们需要使用一个符合GLSL语法的数据格式,Javascript中可用Float32Array类型来创建支持GLSL的数据。使用bufferData函数将数据放入缓存区内。
gl.bufferData(target, size, usage)
target: 同上
size: 为多个顶点坐标的集合数组
usage: 表示程序将如何使用缓存区中的数据
- gl.STATIC_DRAW : 只会向缓存区对象中写入一次数据,但需要绘制很多次
- gl.STREAM_DRAW : 只会向缓存区对象中写入一次数据,然后绘制若干次
- gl.DYNAMIC_DRAW : 会想缓存区对象中多次写入数据,并绘制很多次
缓存区中已经存储了多个顶点坐标,接下来我们需要将此数据运用到对应的着色器上,才能真正的绘制出来可视化图像,如何传递呢?首先我们需要在着色器中建立一个attribute类型的变量以方便我们操作,着色器中的对象,着色器中存在对象之后,我们可以使用Javascript中getAttribLocation函数获取着色器中的attribute类型变量,并且通过vertexAttribPointer将其赋值改变,从而达到改变图像呈现。
gl.getAttribLocation(program,name)
param: webgl之前创建的进程
name: 变量名称
gl.vertexAttribPointer(name, size, type, normalized, stride, offset)
name: 指定要赋值的attribute变量位置
size: 指定每个顶点数据的分量个数(1或4)
type: 指定传入的数据格式
- gl.BYTE: 字节型, 取值范围[-128, 127]
- gl.SHORT: 短整型,取值范围[-32768, 32767]
- gl.UNSIGNED_BYTE: 无符号字节型,取值范围[0, 255]
- gl.UNSIGNED_SHORT: 无符号短整型, 取值范围[0, 65535]
- gl.FLOAT: 浮点型
normalized: 表明是否将非浮点数的数据归入到[0, 1]或[-1, 1]区间
stride: 指定相邻2个顶点间的字节数,默认为0
offset: 指定缓存区对象中的偏移量,设置为0即可1
2
3
4
5
6
7
8
9
10
11如为2,则
new Float32Array([
1.0, 1.0,
1.0,1.0
])
代表2个顶点
如为4,则
new Float32Array([
1.0, 1.0, 1.0,1.0
])
代表1个顶点
现在缓存区已经存在多个顶点数据,接下来我们来启用携带缓存区数据的attribute变量,使用enableVertexAttribArray来启用对应变量。
gl.enableVertexAttribArray(name)
name: 待启动的变量指针,也就是名称
所有的缓存区操作步骤我们都已经完成,那么接下来我们可以绘制出缓存区中的多个顶点
gl.drawArrays(mode, first, count)
mode: 需要绘制的图像形状
- gl.POINTS: 绘制一个点。
- gl.LINE_STRIP: 绘制一条直线到下一个顶点。
- gl.LINE_LOOP: 绘制一条首尾相连的线。
- gl.LINES: 绘制一条线。
- gl.TRIANGLES: 绘制一个三角形。
first: 绘制的开始点
count: 需要绘制的图形个数
看看屏幕吧,是不是出来了好多点。