WebGL 2.0 Vertex Array Objects 使用介绍

852 阅读2分钟

       在WebGL 2.0中,终于把VAO转正了。在WebGL 1.0中,可以通过拓展实现。Babylon.js引擎默认情况下,每次渲染都使用VAO完成。简单介绍下VAO,下面截取自《OpenGLES 3.0 Programming Guide》。

In OpenGL ES 3.0, a new feature was introduced to make using vertex arrays even more efficient: vertex array objects (VAOs). As we have seen, setting up drawing using vertex buffer objects can require many calls to glBindBuffer, glVertexAttribPointer, and glEnableVertexAttribArray. To make it faster to switch between vertex array configurations, OpenGL ES 3.0 introduced vertex array objects. VAOs provide a single object that contains all of the state required to switch between vertex array/vertex buffer object configurations.

     那其实VAO主要是将一个绘制物体的各种顶点缓冲(VBO)包装一个整体。顶点缓冲可以有顶点坐标,纹理坐标等。优点就在于能提高开发效率,它将所有顶点绘制过程中的这些顶点的设置和绑定过程集中存储在一起,当我们需要时,只需要使用相应的VAO即可。VAO的这种方式有点像一个中介,把所有繁琐的绑定和顶点设置工作都集中起来处理,我们需要绘制时,直接找这个中介就好了。那接下来。我们来看看WebGL 2.0中关于VAO的API信息。

Vertex Array Objects [3.7.17]

VAOs encapsulate all state related to the definition of data used by the vertex processor.

void bindVertexArray( WebGLVertexArrayObject? vertexArray);

WebGLVertexArrayObject? createVertexArray();

void deleteVertexArray( WebGLVertexArrayObject? vertexArray);

[WebGLHandlesContextLoss] boolean isVertexArray( WebGLVertexArrayObject? vertexArray);

      有了API的了解,我们来看看大体的绘制流程吧,其实也很简单拉。

       1.创建不同的顶点缓冲(VBO),像顶点坐标,纹理坐标等等

       2. 利用createVertex()创建VAO。

       3.调用bindVertexArray绑定VAO,之后就是日常操作,调用enableVertexAttribArray(),bindBuffer(),vertexAttribPointer()等影响绑定的VAO,也就是组装成一个VAO。

        4.在绘制物体的时候 ,我们只需指定VAO即可。

     那接下我们看案例吧,这个是运行效果图。

首先,我们看看创建VBO的代码,对应上面的步骤1,这里也仅仅是给个代码Demo,不全。

//创建顶点VBO
this.vertexBuffer=gl.createBuffer();				
gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer); 	
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(this.vertexData),gl.STATIC_DRAW);

 下面是代码,将多个VBO(顶点坐标和纹理坐标)包装成1个VAO。对应步骤2和3。

    this.program=programIn;		         //着色器程序
    this.VAOArray=gl.createVertexArray();        //创建VAO数组对象
    gl.bindVertexArray(this.VAOArray);           //绑定VAO

    //启用顶点坐标数据数组
    gl.enableVertexAttribArray(gl.getAttribLocation(this.program, "aPosition"));
    //绑定顶点坐标数据缓冲
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    //给管线指定顶点坐标数据
    gl.vertexAttribPointer(gl.getAttribLocation(this.program, "aPosition"), 3, gl.FLOAT, false, 0, 0);

    //启用顶点纹理坐标数据数组
    gl.enableVertexAttribArray(gl.getAttribLocation(this.program, "aTexCoor"));
    //绑定顶点纹理坐标数据缓冲
    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
    //给管线指定顶点纹理坐标数据
    gl.vertexAttribPointer(gl.getAttribLocation(this.program, "aTexCoor"), 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, null);
    gl.bindVertexArray(null)

现在 我们来看绘制的方法。对应步骤4,绘制物体的时候指定VAO

this.drawSelf=function(ms,texture)//绘制物体的方法
{
	//指定使用某套着色器程序	
	gl.useProgram(this.program);	
		
	//获取总变换矩阵引用id
	var uMVPMatrixHandle=gl.getUniformLocation(this.program, "uMVPMatrix");
	//将总变换矩阵送入渲染管线		
	gl.uniformMatrix4fv(uMVPMatrixHandle,false,new Float32Array(ms.getFinalMatrix()));

        //设置VAO
        gl.bindVertexArray(this.VAOArray);

        gl.activeTexture(gl.TEXTURE0);//设置使用的纹理编号-0
        gl.bindTexture(gl.TEXTURE_2D, texture);//绑定纹理
        gl.uniform1i(gl.getUniformLocation(this.program, "sTexture"), 0);//纹理送入管线
        
        //用顶点法绘制物体
	gl.drawArrays(gl.TRIANGLES, 0, this.vcount);
		
        gl.bindVertexArray(null)
} 

最后要说的是gl.bindVertexArray(null)主要是解绑VAO,不解绑的话会影响后面的很多操作。