Cesium原理篇:6 Renderer模块(1: Buffer)
Cesium原理篇:6 Renderer模块(2 Texture)
Cesium原理篇:6 Render模块(3 Shader)
Cesium原理篇:6 Render模块(4 FBO)
Cesium原理篇:6 Render模块(5 VAO&RenderState&Command)
Cesium原理篇:6 Render模块(6 Instance实例化)
参考:https://www.cnblogs.com/fuckgiser/p/6027520.html
渲染器:
要讲3D Tiles,或者充分理解它,需要对DataSource,Primitive要有基础,而这要求对最底层的渲染模块,也就是Render有一个了解。
1. Buffer
当我们想要通过WebGL渲染该几何对象时,首先就是要讲该几何对象转化为WebGL可以识别的数据格式:(1)构建该对象的顶点数组,里面包括每一个点的XYZ位置(必须),该点的颜色,纹理坐标,法线等信息;(2)构建该对象对应的索引信息,也就是点之间的先后顺序。
上述说的就是一个VBO(顶点缓存)的概念,WebGL提供了bufferData接口,我们对其中的参数做一个简单介绍
//获取WebGL对象 var canvas = document.getElementById("canvas"); var gl = canvas.getContext("webgl"); //创建顶点缓存 var buffer = gl.createBuffer(); //绑定该顶点缓存类型为顶点属性数据 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); //指定其指定的数据长度及方式 gl.bufferData(gl.ARRAY_BUFFER,1024,gl.STATIC_DRAW); //解除绑定 gl.bindBuffer(bufferTarget, null);
通过上面的介绍,可见顶点属性和顶点索引的调用方法完全相同,逻辑上的创建过程也如出一辙,只是具体的参数稍有不同,因此,在Cesium中把创建VBO的过程化的函数封装为一个抽象的Buffer类,伪代码如下:
function Buffer(options) { var gl = options.context._gl; var bufferTarget = options.bufferTarget; var typedArray = options.typedArray; var sizeInBytes = options.sizeInBytes; var usage = options.usage; var hasArray = defined(typedArray); if (hasArray) { sizeInBytes = typedArray.byteLength; } // …… var buffer = gl.createBuffer(); gl.bindBuffer(bufferTarget, buffer); gl.bufferData(bufferTarget, hasArray ? typedArray : sizeInBytes, usage); gl.bindBuffer(bufferTarget, null); // …… this._gl = gl; this._bufferTarget = bufferTarget; this._sizeInBytes = sizeInBytes; this._usage = usage; this._buffer = buffer; this.vertexArrayDestroyable = true; }
可见,这个过程和刚才WebGL调用的方式几乎一模一样,只是把所需要的参数都封装了一下,并将调用函数的返回值,作为属性保存在该Buffer对象中。于是,一个过程式的函数调用封装成了一个Object,即可以重用,也方便内部细节的管理。
由于Options参数太多,不方便理解。于是接着Cesium在Buffer类中提供了Buffer.createVertexBuffer和Buffer.createIndexBuffer方法
Buffer.createVertexBuffer = function(options) { return new Buffer({ context: options.context, bufferTarget: WebGLConstants.ARRAY_BUFFER, typedArray: options.typedArray, sizeInBytes: options.sizeInBytes, usage: options.usage }); }; Buffer.createIndexBuffer = function(options) { return new Buffer({ context: options.context, bufferTarget : WebGLConstants.ELEMENT_ARRAY_BUFFER, typedArray : options.typedArray, sizeInBytes : options.sizeInBytes, usage : options.usage }); };
这下用起来就so easy了,用户创建顶点属性和顶点索引的范例如下:
var buffer = Buffer.createVertexBuffer({ context : context, sizeInBytes : 16, usage : BufferUsage.DYNAMIC_DRAW }); var buffer = Buffer.createIndexBuffer({ context : context, typedArray : new Uint16Array([0, 1, 2]), usage : BufferUsage.STATIC_DRAW, indexDatatype : IndexDatatype.UNSIGNED_SHORT });
当然,如果你此时是用的DYNAMIC_DRAW的方式,并没有指定bufferdata,则创建该顶点缓存对象,但数据还是空的,在获取数据后可以调用Buffer.prototype.copyFromArrayView方法更新一下该数据。可见,Buffer类的设计还是比较周全的,考虑到数据更新可能存在这种不确定的逻辑:
Buffer.prototype.copyFromArrayView = function(arrayView, offsetInBytes) { var gl = this._gl; var target = this._bufferTarget; gl.bindBuffer(target, this._buffer); gl.bufferSubData(target, offsetInBytes, arrayView); gl.bindBuffer(target, null); };
如上就是创建VBO的一个完整过程,可以仔细对比一下两者之间的不同。因为VBO是渲染中最基本的,也是最重要的概念和渲染对象,通过Buffer类对这个过程进行分装和管理,虽然难度不大,但意义却很重要。
通过Buffer,我们可以将Primitive(图元)中的几何数据转化为VBO,这相当于创建了该几何对象的骨架。通常我们还需要纹理信息,贴在这个骨架的表面,让它看上去有血有肉,惟妙惟肖。下一篇我们介绍Cesium是如何封装Texture。