webgl扩展系列之一------------OES_vertex_array_object

311 阅读3分钟

一.扩展介绍

OES_vertex_array_object 是一个WebGL扩展,用于引入顶点数组对象(VAO),以便更有效地组织和管理顶点属性数据和状态。

顶点数组对象(VAO)是一种用于存储和管理顶点数据和属性状态的机制,它可以将顶点属性配置和顶点缓冲绑定组合在一起。这有助于减少重复性的代码,提高性能,并使代码更易于维护。

二.简单实例-不使用扩展绘制两个四边形

  1. 首先创建两个四边形的顶点数据缓冲区
 //创建第一个对象的顶点数据
function initVertexBuffer1(gl) {
    //创建位置缓冲区
    var positions = new Float32Array([
        1, 0, 0, 
        0, 1, -1,
        -1, 0, -1, 
        0, -1, 0,
    ])
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)


    //创建颜色缓冲区
    var colors = new Float32Array([
        0, 0, 1,
        1, 0, 0,
        0, 0, 1,
        1, 0, 0,
    ])
    var colorBuffer = gl.createBuffer();
    var fSize = positions.BYTES_PER_ELEMENT
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)

    //创建索引缓冲区
    const indexes=new Uint8Array([
        0,1,2,
        2,3,0
    ])

    const indexBuffer=gl.createBuffer()
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW)
    return {
        positionBuffer,
        colorBuffer,
        indexBuffer,
        fSize
    }

}

//创建第二个对象的顶点数据
function initVertexBuffer2(gl) {
    //创建顶点缓冲区
    var positions = new Float32Array([
        1, 0, 0, 
        0, 1, 1, 
        -1, 0, 1, 
        0, -1, 0,
    ])
    //创建缓冲区
    var positionBuffer = gl.createBuffer();
    var fSize = positions.BYTES_PER_ELEMENT
    //将缓冲区绑定到顶点缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    //向缓冲区写入数据,静态:不怎么变
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)



    //创建颜色缓冲区
    var colors = new Float32Array([
        1, 1, 1,
        1, 1, 1,
        1, 1, 1,
        1, 1, 1,
    ])
    var colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)

    //创建索引缓冲区
    const indexes=new Uint8Array([
        0,1,2,
        2,3,0
    ])

    const indexBuffer=gl.createBuffer()
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW)
    return {
        positionBuffer,
        colorBuffer,
        indexBuffer,
        fSize
    }

}

2.绘制时再次绑定切换缓冲区

const objBuffer=initVertexBuffer1(gl)
const objBuffer1=initVertexBuffer2(gl)

//绑定一个对象的顶点和索引缓冲区
function bindObjBuffer(gl,objBuffer){
    const fSize=objBuffer.fSize
    gl.bindBuffer(gl.ARRAY_BUFFER,objBuffer.positionBuffer)     
    var a_position = gl.getAttribLocation(gl.program, 'a_position');     
    gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, fSize * 3, 0)
    gl.enableVertexAttribArray(a_position)


    gl.bindBuffer(gl.ARRAY_BUFFER,objBuffer.colorBuffer)
    var a_color = gl.getAttribLocation(gl.program, 'a_color');
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, fSize *3, 0)
    gl.enableVertexAttribArray(a_color)
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,objBuffer.indexBuffer)
}


 //根据时间绘制
var tick = function () {
    //变换角度
    currentAngle+=0.2
    gl.clear(gl.COLOR_BUFFER_BIT)
    //////////////////////普通方式绘制///////////////////////////////
     //绑定第一个四边形
     bindObjBuffer(gl,objBuffer)
     draw(gl, currentAngle, modelMatrix, u_modelMatrix)
     //绑定第二个四边形
    bindObjBuffer(gl,objBuffer1)
     draw(gl, 0, modelMatrix, u_modelMatrix)
    //////////////////////普通方式绘制///////////////////////////////
    requestAnimationFrame(tick)
}
tick()

这儿对绑定缓冲区做了一个封装bindObjBuffer(),因为都只有positionBuffer,colorBufferindexBuffer,但是如果不一致的话这儿就不能做封装,代码会更加复杂

三.使用扩展绘制

1.获取扩展

//获取扩展
var ext = gl.getExtension("OES_vertex_array_object");

2.创建顶点缓冲区数组 const vao =ext.createVertexArrayOES(); ext.bindVertexArrayOES(vao);

//创建第一个对象的顶点数据
function initVertexArrayObject1(gl,ext) {
    //创建顶点缓冲区数组
    const vao =ext.createVertexArrayOES();
    ext.bindVertexArrayOES(vao);

    //创建位置缓冲区
    var positions = new Float32Array([
        1, 0, 0, 
        0, 1, -1,
        -1, 0, -1, 
        0, -1, 0,
    ])
    var fSize = positions.BYTES_PER_ELEMENT
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
    var a_position = gl.getAttribLocation(gl.program, 'a_position');     
    gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, fSize * 3, 0)
    gl.enableVertexAttribArray(a_position)


    //创建颜色缓冲区
    var colors = new Float32Array([
        0, 0, 1,
        1, 0, 0,
        0, 0, 1,
        1, 0, 0,
    ])
    var colorBuffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)
    var a_color = gl.getAttribLocation(gl.program, 'a_color');
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, fSize *3, 0)
    gl.enableVertexAttribArray(a_color)

    //创建索引缓冲区
    const indexes=new Uint8Array([
        0,1,2,
        2,3,0
    ])

    const indexBuffer=gl.createBuffer()
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW)
    return vao
}



 //创建第一个对象的顶点数据
 function initVertexArrayObject2(gl,ext) {
    //创建顶点缓冲区数组
    const vao =ext.createVertexArrayOES();
    ext.bindVertexArrayOES(vao);

    //创建位置缓冲区
    var positions = new Float32Array([
        1, 0, 0, 
        0, 1, 1, 
        -1, 0, 1, 
        0, -1, 0,
    ])
    var fSize = positions.BYTES_PER_ELEMENT
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
    var a_position = gl.getAttribLocation(gl.program, 'a_position');     
    gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, fSize * 3, 0)
    gl.enableVertexAttribArray(a_position)


    //创建颜色缓冲区
    var colors = new Float32Array([
        1, 1, 1,
        1, 1, 1,
        1, 1, 1,
        1, 1, 1,
    ])
    var colorBuffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)
    var a_color = gl.getAttribLocation(gl.program, 'a_color');
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, fSize *3, 0)
    gl.enableVertexAttribArray(a_color)

    //创建索引缓冲区
    const indexes=new Uint8Array([
        0,1,2,
        2,3,0
    ])

    const indexBuffer=gl.createBuffer()
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW)
    return vao
}

3.绑定绘制

const vao=initVertexArrayObject1(gl,ext)
const vao1=initVertexArrayObject2(gl,ext)
 //根据时间绘制
var tick = function () {
    //变换角度
    currentAngle+=0.2
    gl.clear(gl.COLOR_BUFFER_BIT)
     //绑定第一个四边形
    //bindObjBuffer(gl,objBuffer)
    //draw(gl, currentAngle, modelMatrix, u_modelMatrix)
    //绑定第二个四边形
    //bindObjBuffer(gl,objBuffer1)
    //draw(gl, 0, modelMatrix, u_modelMatrix)
    ///////////////////////////扩展绘制/////////////////////////////////////
     ext.bindVertexArrayOES(vao);
     draw(gl, currentAngle, modelMatrix, u_modelMatrix)
     ext.bindVertexArrayOES(vao1);
     draw(gl, 0, modelMatrix, u_modelMatrix)
    ///////////////////////////扩展绘制/////////////////////////////////////
    requestAnimationFrame(tick)
}
tick()

通过上述代码可以看出,vao有点类似于objBuffer,如果可以封装objBuffer绘制时绑定,代码非常类似,但是在性能上却不一样,毕竟objBuffer要重复绑定好多次,何况大多数情况下并不能封装

四.在webgl2中的情况

在webgl2 中,VAO已经作为官方标准一部分,不以扩展方式存在,具体用法如下:

1.创建绑定vao改为如下写法:

var vao = gl.createVertexArray(); 
gl.bindVertexArray(vao);

2.绘制时

gl.bindVertexArray(vao); 
draw(gl, currentAngle, modelMatrix, u_modelMatrix) 

gl.bindVertexArray(vao1);
draw(gl, 0, modelMatrix, u_modelMatrix)