美文网首页
02-WebGL缓冲区对象

02-WebGL缓冲区对象

作者: learninginto | 来源:发表于2021-07-23 18:09 被阅读0次

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

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

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

    1. 创建缓冲区对象gl.createBuffer()
    2. 绑定缓冲区对象gl.bindBuffer()
    3. 将数据写入缓冲区对象gl.bufferData()
    4. 将缓冲区对象分配给一个attribute变量gl.vertexAttribPointer()
    5. 开始attribute变量gl.enableVertexAttribArray()
    • 绘制三角形的3个顶点
    // 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就是其中之一。

    • WebGL使用的各种类型化数组
    数组类型 每个元素所点字节数 描述(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个点绘制出一个三角形。

    • WebGL可以绘制的基本图形
    image.png
    • 对应效果
    image.png

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

    相关文章

      网友评论

          本文标题:02-WebGL缓冲区对象

          本文链接:https://www.haomeiwen.com/subject/gkepmltx.html