最近研究Cesium的实例化,尽管该技术需要在WebGL2.0,也就是OpenGL ES3.0才支持。调试源码的时候眼前一亮,发现VAO和glDrawBuffers都不是WebGL1.0的标准函数,都是扩展功能,看来WebGL2.0标准的推广势在必行啊。同时发现,通过ANGLE_instanced_arrays的扩展,也可以在WebGL1.0下实现实例化,创建实例化方法的代码如下:
varglDrawElementsInstanced;varglDrawArraysInstanced;varglVertexAttribDivisor;varinstancedArrays;//WebGL2.0标准直接提供了实例化接口
if(webgl2) {
glDrawElementsInstanced= function(mode, count, type, offset, instanceCount) {
gl.drawElementsInstanced(mode, count, type, offset, instanceCount);
};
glDrawArraysInstanced= function(mode, first, count, instanceCount) {
gl.drawArraysInstanced(mode, first, count, instanceCount);
};
glVertexAttribDivisor= function(index, divisor) {
gl.vertexAttribDivisor(index, divisor);
};
}else{//WebGL1.0下
//扩展ANGLE_instanced_arrays
instancedArrays = getExtension(gl, ['ANGLE_instanced_arrays']);if(defined(instancedArrays)) {
glDrawElementsInstanced= function(mode, count, type, offset, instanceCount) {
instancedArrays.drawElementsInstancedANGLE(mode, count, type, offset, instanceCount);
};
glDrawArraysInstanced= function(mode, first, count, instanceCount) {
instancedArrays.drawArraysInstancedANGLE(mode, first, count, instanceCount);
};
glVertexAttribDivisor= function(index, divisor) {
instancedArrays.vertexAttribDivisorANGLE(index, divisor);
};
}
}//涉及到实例化的三个方法
this.glDrawElementsInstanced =glDrawElementsInstanced;this.glDrawArraysInstanced =glDrawArraysInstanced;this.glVertexAttribDivisor =glVertexAttribDivisor;this._instancedArrays = !!instancedArrays;
通过这样的封装,Cesium.Context提供了标准的实例化方法,不需要用户过多的关心WebGL标准的差异。而实例化的渲染也非常简单,核心代码如下:
functioncontinueDraw(context, drawCommand) {//……
var instanceCount =drawCommand.instanceCount;if(defined(indexBuffer)) {
offset= offset *indexBuffer.bytesPerIndex;//offset in vertices to offset in bytes
count =defaultValue(count, indexBuffer.numberOfIndices);if (instanceCount === 0) {
context._gl.drawElements(primitiveType, count, indexBuffer.indexDatatype, offset);
}else{
context.glDrawElementsInstanced(primitiveType, count, indexBuffer.indexDatatype, offset, instanceCount);
}
}else{
count=defaultValue(count, va.numberOfVertices);if (instanceCount === 0) {
context._gl.drawArrays(primitiveType, offset, count);
}else{
context.glDrawArraysInstanced(primitiveType, offset, count, instanceCount);
}
}//……
}
是否实例化渲染,取决于你所构造的DrawCommand是否有实例化的信息,对应代码中的drawCommand.instanceCount,如果你的实例化数目不为零,则进行实例化的渲染。因此,Context中对实例化进行了封装,内部的渲染机制中,实例化和非实例化的渲染机制差别并不大。从应用的角度来看,我们并不需要关心Context的实现,而是通过构造DrawCommand来决定是否想要实例化渲染。
之前我们较详细的介绍过Renderer.DrawCommand模块,如果不清楚的回去再翻翻看,在VertexArray中实现了VAO中创建attr.vertexAttrib,这里有一个instanceDivisor属性,这就是用来表示该attribute是否是实例化的divisor:
attr.vertexAttrib = function(gl) {var index = this.index;
gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer._getBuffer());
gl.vertexAttribPointer(index,this.componentsPerAttribute, this.componentDatatype, this.normalize,this.strideInBytes, this.offsetInBytes);
gl.enableVertexAttribArray(index);if (this.instanceDivisor > 0) {
context.glVertexAttribDivisor(index,this.instanceD