webgl 5.画3个点
前面我们都只传递了一个顶点到 vertex shader 中,现在来看看怎么一次传递多个顶点进去
draw three points.pngshader 不变
// vertex shader
var VERTEX_SHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
// fragment shader
var FRAGMENT_SHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n' +
'}\n';
下面来定义 3 个顶点
var vertices = new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]);
这里用了 Float32Array
为了高效的处理数据,必须使用 Typed Array, 支持以下类型
Int8Array 1个字节
Uint8Array 1个字节
Int16Array 2个字节
Uint16Array 2个字节
Int32Array 4个字节
Uint32Array 4个字节
Float32Array 4个字节
Float64Array 8个字节
为了一次传递多个顶点到 vertex shader 中,需要用到 Buffer Object, 看看下图
Buffer Object.pngBuffer Object 其实就是 GPU 中开辟的一块内存,用来存储顶点信息
function initVertexBuffers(gl, vertices) {
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create buffer object');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// write data to the buffer object
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 属性变量和数据关联起来并指定解析方法
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// enable the assignment to attribute variable
gl.enableVertexAttribArray(a_Position);
}
- gl.createBuffer()
create 一个 buffer object
- gl.bindBuffer(target, buffer)
将 buffer 绑定到 target 上
target 有 2 种取值: gl.ARRAY_BUFFER 标识 vertex object 里面存的是顶点
gl.ELEMENT_ARRAY_BUFFER 标识 vertex object 里面存的是顶点的索引值
- gl.bufferData(target, data, usage)
将数据 data 拷贝到绑定到 target 的 buffer object 中。
这里才真在 GPU 中分配内存并从主存拷贝数据,gl.createBuffer() 可以理解为创建了一个指针,这里才知道要分配多少内存。
usage: 提示数据将被怎么使用。可取 gl.STATIC_DRAW 表示数据会被写一次然后多次使用, gl.STREAM_DRAW 表示数据会被写一次然后使用几次, gl.DYNAMIC_DRAW 表示数据会被多次写入。 这个主要是提高性能用的,仅仅是给 GPU 一个建议,设置错了也没事。
- gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
将 attribute 变量和 buffer object 关联起来,并指定怎么解析数据。
location: 要关联的 attribute 地址
size:表示每个顶点你为 attribute 传递了几个 components,因为每个顶点我们只传了 (x, y) 2 个值,所以这里 size 为 2 ,z 和 w 会取默认值。 显然 size 的值只能是 1-4。
type: 指定数据类型
normalized : Either true or false to indicate whether nonfloating data should be normalized to [0, 1] or [–1, 1] 。
stride 各个顶点属性之间的间隔, 单位是字节
offset 第一个顶点属性的起始位置, 单位是字节
数据类型可以取以下值:
gl.UNSIGNED_BYTE 对应 Uint8Array
gl.SHORT 对应 Int16Array
gl.UNSIGNED_SHORT 对应 Uint16Array
gl.INT 对应 Int32Array
gl.UNSIGNED_INT 对应 Uint32Array
gl.FLOAT 对应 Float32Array
之前说过一个顶点里面可以包括顶点的 position (坐标)、color(颜色)、norm(法向量) 、texture coord(纹理坐标) 等信息。通过 size, type, stride, offset 我们就能循环从 vertex buffer 中提取中各个数据并传给对应的 attribute 变量。
- gl.drawArrays(gl.POINTS, 0, 3)
从第 0 个顶点开始,循环处理 3 个顶点
每个顶点都会调用一遍 vertex shader
练习:
- 试试 gl.drawArrays(gl.POINTS, 0, 1), gl.drawArrays(gl.POINTS, 2, 1),gl.drawArrays(gl.POINTS, 2, 2)
思考:
- 我想让每个顶点有不同的颜色怎么做?显然除了位置还要把每个顶点的颜色传进去,而传递颜色信息有 2 种方法,一是我可以再定义一个 buffer object 用来传递颜色,二是我把顶点的位置和颜色混在一块一起传过去然后再解析, 请看下节