02-WebGL缓冲区对象

2021-07-23  本文已影响0人  learninginto

WebGL提供了一种很方便的机制,即缓冲区对象(buffer object),它可以一次性地向着色器传入多个顶点的数据。

缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。

使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循以下5个步骤:

  1. 创建缓冲区对象gl.createBuffer()
  2. 绑定缓冲区对象gl.bindBuffer()
  3. 将数据写入缓冲区对象gl.bufferData()
  4. 将缓冲区对象分配给一个attribute变量gl.vertexAttribPointer()
  5. 开始attribute变量gl.enableVertexAttribArray()
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Write the positions of vertices to a vertex shader
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the positions of the vertices');
    return;
  }

  // Specify the color for clearing <canvas>
  gl.clearColor(0, 0, 0, 1);

  // Clear <canvas>
  gl.clear(gl.COLOR_BUFFER_BIT);

  // Draw three points
  gl.drawArrays(gl.POINTS, 0, n);
}

function initVertexBuffers(gl) {
  var vertices = new Float32Array([
    0.0, 0.5,   -0.5, -0.5,   0.5, -0.5
  ]);
  var n = 3; // The number of vertices

  // 创建缓冲区对象
  var vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }

  // 将缓冲区对象绑定到目标
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  // 向缓冲区对象中写入数据
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  // 将缓冲区对象分配给a_Position变量
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

  // 连接a_Position变量与分配给它的缓冲区对象
  gl.enableVertexAttribArray(a_Position);

  return n;
}
  1. 创建缓冲区对象gl.createBuffer()

执行该方法的结果就是,WebGL系统中多了一个新创建出来的缓冲区对象。返回值为null表示创建失败。

相应地,gl.deleteBuffer(buffer)函数可以用来删除被gl.createBuffer()创建出来的缓冲区对象

  1. 绑定缓冲区gl.bindBuffer(target,buffer)

创建缓冲区的第二步就是将缓冲区对象绑定到WebGL系统中已经存在的“目标”上。这个“目标”表示缓冲区对象的用途。允许使用buffer表示的缓冲区对象绑定到target表示的目标上。

gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)

其中,gl.ARRAY_BUFFER表示缓冲区对象中包含了顶点的数据

  1. 向缓冲区对象中写入数据gl.bufferData(target,data,usage)

第三步,开辟空间并向缓冲区中写入数据。

var vertices = new Float32Array([
    0.0,0.5,-0.5,-0.5,0.5,0.5
])
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

该方法的效果是,将第2个参数vertices中的数据写入绑定到第1个参数gl.ARRAY_BUFFER上的缓冲区对象。我们不能直接向缓冲区写入数据,而只能向"目标"写入数据,所以要向缓冲区写数据,必须先绑定。

其中,参数usage表示程序将如何使用存储在缓冲区对象中的数据。该参数将帮助WebGL优化操作,但即便传入了错误的值,也不会终止程序(仅仅是降低程序的效率)

上面,我们使用了Float32Array对象,而不是JS更常见的Array对象。这是因为,JS中的数组Array是一种通用的类型,既可以存储数字也可以存储字符串,而并没有对“大量元素都是同一种类型”优化。为了解决这个问题,WebGL引入了类型化的数组,Float32Array就是其中之一。

数组类型 每个元素所点字节数 描述(C语言中的数据类型)
Int8Array 1 8位整型(singed char)
UInt8Array 1 8位无符号整型(unsinged char)
Int16Array 2 16位整型(singed short)
UInt16Array 2 16位无符号整型(unsinged short)
Int32Array 4 32位整型(singed int)
UInt32Array 4 32位无符号整型(unsinged int)
Float32Array 4 单精度32位浮点数(float)
Float64Array 8 双精度64位浮点数(double)

注意: 与普通的Array数组不同,类型化数组不支持push()和pop()方法;创建类型化数组的唯一方法就是使用new运算符,不能使用[]运算符。

方法、属性和常量 描述
get(index) 获取第index个元素值
set(index,value) 设置第index个元素的值为value
set(array,offset) 从第offset个元素开始将数组array中的值填充进去
length 数组的长度
BYTES_PER_ELEMENT 数组中每个元素所占的字节数
  1. 将缓冲区对象分配给attribute变量gl.vertexAttribPointer(location,size,type,normalized,stride,offset)

将绑定到gl.ARRAY_BUFFER的缓冲区对象分配给由location指定的attribute变量。

  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

参数location指定待分配attribute变量的存储位置;

size指定缓冲区中每个顶点的分量个数(1到4),若size比attribute变量需要的分量数小,缺失分量将按照与gl.vertexAttrib[1234]f()相同的规则补全。(比如:size为1,那么第2、3分量自动设为0,第4分量为1)

type为数据类型

normalize传入true或false,表明是否将非浮点型的数据归一化到[0,1]或[1,1]区间

stride指定相邻两个顶点间的字节数,默认为0

offset指定缓冲区对象中的偏移量

  1. 开启attribute变量gl.enableVertexAttribArray()
gl.enableVertexAttribArray(a_Position)

注意:虽然函数的名称似乎表示该函数是用来处理“顶点数组”的,但实际上它处理的对象是缓冲区。这是由于历史原因(从OpenGL中继承)造成的。

开启attribute变量后,就不能再用gl.vertexAttrib[1234]f()向它传数据了,除非你显示地关闭该attribute变量。实际上,你无法(也不应该)同时使用这两个函数。

gl.drawArrays(mode,first,count)

mode指定绘制的方式,可接收:gl.POINTS、gl.LINES、gl.LINES_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRINGLE_FAN

first指定从哪个顶点开始绘制(整数)

count 指定绘制需要用到多少个顶点(整数)

gl.drawArrays(gl.POINTS,0,n) //n为3

实际上,顶点着色器执行了n(3)次,我们通过存储在缓冲区中的顶点坐标数据被依次传给attribute变量。

基于上面顶点程序的改动有两处:

1.在顶点着色器中,去掉指定点的尺寸gl_PointSize = 10.0;,该语句只有在绘制单个点的时候才起作用。

2.gl.drawArrays()方法的第1个参数从gl.POINTS被改为了gl.TRINGLES,就相当于告诉WebGL,从缓冲区中的第1个顶点开始,使顶点着色器执行3次(n为3),用这3个点绘制出一个三角形。

image.png image.png

WebGL有点上头,暂时告一段落……

上一篇下一篇

猜你喜欢

热点阅读