上次说到了对VAO、VBO的理解,明白了使用VAO相比传统模式在IO方面对加载性能的提升,今天我对其在WebGL上进行了实现。
在WebGL中由于很多API与OpenGLES大为不同(一般常用的API大致是相同的),开始时费了很多劲,最后还是通过MSDN找到了相关的API及方法,这里对设计VAO的主要的部分进行介绍,对VAO还不是很理解的同学可以看这篇文章:http://blog.csdn.net/srk19960903/article/details/74999018
这次我们还用的是上次那个篮球在地面上弹的例子,只是把篮球和地面加载的部分换成了VAO方法,篮球阴影的加载还使用的是传统方法,以便于对照。
function ObjObject ( gl, //GL上下文 vertexDataIn, //顶点坐标数组 vertexNormalIn, //顶点法向量数组 vertexTexCoorIn, //顶点纹理坐标数组 programIn //着色器程序对象 ) { //加载着色器程序 this.program=programIn; this.ext = gl.getExtension("OES_vertex_array_object"); //create VAO this.vao = this.ext.createVertexArrayOES(); // bind vao this.ext.bindVertexArrayOES(this.vao); //接收顶点数据 this.vertexData=vertexDataIn; //得到顶点数量 this.vcount=this.vertexData.length/3; //创建顶点数据缓冲 this.vertexBuffer=gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(this.vertexData),gl.STATIC_DRAW); var apos = gl.getAttribLocation(this.program, "aPosition"); gl.vertexAttribPointer(apos, 3, gl.FLOAT, false, 0, 0); //启用顶点数据 gl.enableVertexAttribArray(apos); //此处省略法线和纹理坐标的数据 this.ext.bindVertexArrayOES(null);}
这里可以看到在初始化的部分代码明显变多,主要是因为将每次传递顶点、法线、纹理坐标位置的数据从drawSelf移到了初始化函数中,这里值得一提的是,在OpenGLES中的Gen开头的API在WebGL中都没有,取而代之的是gl.getExtension("OES_vertex_array_object");然后创建VAO并对其进行绑定,最后在完成之后取消绑定。还有就是一定要注意顺序,每次创建数据缓冲、启用顶点数据和绑定缓冲的代码必须一次完成,之前我写的时候总是报:
WebGL GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
下面是drawSelf的方法:
this.drawSelf=function(ms,texture) { gl.useProgram(this.program);//指定使用某套着色器程序(重要) //送入总矩阵 var uMVPMatrixHandle=gl.getUniformLocation(this.program, "uMVPMatrix"); gl.uniformMatrix4fv(uMVPMatrixHandle,false,new Float32Array(ms.getFinalMatrix())); //送入变换矩阵 var uMMatrixHandle=gl.getUniformLocation(this.program, "uMMatrix"); gl.uniformMatrix4fv(uMMatrixHandle,false,new Float32Array(ms.currMatrix)); //送入光源位置 var uLightLocationHandle=gl.getUniformLocation(this.program, "uLightLocation"); gl.uniform3fv(uLightLocationHandle,new Float32Array([lightManager.lx,lightManager.ly,lightManager.lz])); //送入摄像机位置 var uCameraHandle=gl.getUniformLocation(this.program, "uCamera"); gl.uniform3fv(uCameraHandle,new Float32Array([ms.cx,ms.cy,ms.cz])); //绑定纹理 gl.bindTexture(gl.TEXTURE_2D, texture); this.ext.bindVertexArrayOES(this.vao); //用顶点法绘制物体 gl.drawArrays(gl.TRIANGLES, 0, this.vcount); }这次在传递完uniform的参数之后,直接打开了bindVertexArrayOES,使用其中的数据当做绘制时需要用的顶点数据、法线数据、纹理坐标数据,这样在每次绘制时就不用再次传递了。
实现的效果与阴影那片相同,主要是将obj加载方法换成了VAO的方式。
下面是Github地址:https://github.com/StringKun/WebGL-VAO