刚刚结束完地球切片的渲染调度后,打算介绍一下目前大家都很关注的3D Tiles方面的内容,但发现要讲3D Tiles,或者充分理解它,需要对DataSource,Primitive要有基础,而这要求对最底层的渲染模块,也就是Render有一个了解,真的是书到用时方恨少的代入感,索性从头到尾的说清楚。所以,下面几篇(估算四篇)先把Render模块介绍清楚。

       Render,顾名思义就是渲染模块,在Cesium中,所有的对象,都是通过Render模块完成最终调用WebGL的渲染过程。换句话说,Render是Cesium的渲染引擎模块,在Cesium的Geometry和WebGL之间搭建了一个桥梁。至于为什么不直接调用WebGL接口,还需要封装一套,这样做有何意义呢,这就是一个比较发散的问题了。首先WebGL提供的是函数,而在真正的渲染中,会有很多状态的切换,而且随着逻辑的负责而难以维护,而面向对象的思想的精髓就在于对对象状态的管理,这也是为什么很多三维应用都提供了自己的渲染引擎模块,个人觉得主要的价值在于易用性和状态的管理。

       首先,目前主流的浏览器基本支持WebGL 1.0的标准,对应的是OpenGL ES2.0,也就是可编程渲染管线,采用GPU渲染,性能上有保证。据我了解,只有FireFox的一些实验版本支持WebGL2.0,也就是OpenGL ES3.0的标准。如果对WebGL的基本接口还不熟悉,推荐这本《WebGL编程指南》,除了感觉价格略贵外,还是一本很不错的入门书。我们的重点是学习Cesium如何设计并封装WebGL接口,并不会过多纠结在WebGL本身的理解,所以假设你对WebGL有一个基本的了解。

Buffer

       任意一个非参数化的几何对象(参数化的可以通过差值转为非参数化,比如一个参数化的圆,对应的是圆心和半径,我们通过差值,将该圆转为一个多边形,圆周对应的点越密,则效果越好,而代价也是点数据越大),对应的就是N个顶点(Node)之间的空间关系。当我们想要通过WebGL渲染该几何对象时,首先就是要讲该几何对象转化为WebGL可以识别的数据格式:1构建该对象的顶点数组,里面包括每一个点的XYZ位置(必须),该点的颜色,纹理坐标,法线等信息;2构建该对象对应的索引信息,也就是点之间的先后顺序。

       上述说的就是一个VBO(顶点缓存)的概念,WebGL提供了bufferData接口,我们对其中的参数做一个简单介绍:

void gl.bufferData(target, size, usage);
void gl.bufferData(target, data, usage);

        

  • target

    gl.ARRAY_BUFFER 该数据为顶点数据,比如位置信息,颜色,法线,纹理坐标或自定义的一些属性信息

    gl.ELEMENT_ARRAY_BUFFER 该数据为顶点索引

  • size

    数据长度,此时只是预留了对应长度的空间,里面的数据为空
  • data

    数据内容,此时WebGL内部可以判断该数据内容对应的长度
  • usage

    gl.STATIC_DRAW 静态数据,数据保存在GPU中,适合一次写入不再更改,多次使用情况

    gl.DYNAMIC_DRAW 动态数据,保存在内存中,适合多次写入,多次使用下调用情况

    gl.STREAM_DRAW 流数据,保存在GPU中,适合一次写入一次使用的情况

       如下是直接调用WebGL提供的方法创建顶点属性的代码:

// 获取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对象中。于是,一个过程式的函数调用封装成了一个Ojbect,即可以重用,也方便内部细节的管理。

       当然,如果只有如上的一个方法也能用,但输入的参数不太少,只是一个很简单的函数封装而已,使用上并没有太多简化,而且也不方便理解,只能算是一个半成品。接着Cesium在Buffer类中提供了Buffer.createVertexBuffer和Buffer.createIndexBuffer方法,算是基于Buffer的一个二次加工:

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。

Cesium原理篇:6 Renderer模块(1: Buffer)的更多相关文章

  1. Cesium原理篇:7最长的一帧之Entity(下)

    上一篇,我们介绍了当我们添加一个Entity时,通过Graphics封装其对应参数,通过EntityCollection.Add方法,将EntityCollection的Entity传递到DataSo ...

  2. Cesium原理篇:5最长的一帧之影像

    如果把地球比做一个人,地形就相当于这个人的骨骼,而影像就相当于这个人的外表了.之前的几个系列,我们全面的介绍了Cesium的地形内容,详见: Cesium原理篇:1最长的一帧之渲染调度 Cesium原 ...

  3. Cesium原理篇:3最长的一帧之地形(2:高度图)

           这一篇,接着上一篇,内容集中在高度图方式构建地球网格的细节方面.        此时,Globe对每一个切片(GlobeSurfaceTile)创建对应的TileTerrain类,用来维 ...

  4. Cesium原理篇:6 Renderer模块(1: Buffer)【转】

    https://www.bbsmax.com/A/n2d9P1Q5Dv/ 刚刚结束完地球切片的渲染调度后,打算介绍一下目前大家都很关注的3D Tiles方面的内容,但发现要讲3D Tiles,或者充分 ...

  5. Cesium原理篇:6 Renderer模块(2: Texture)

    Texture也是WebGL中重要的概念,使用起来也很简单.但有句话叫大道至简,如果真的想要用好纹理,里面的水其实也是很深的.下面我们来一探究竟. 下面是WebGL中创建一个纹理的最简过程: var ...

  6. Cesium原理篇:6 Render模块(6: Instance实例化)

    最近研究Cesium的实例化,尽管该技术需要在WebGL2.0,也就是OpenGL ES3.0才支持.调试源码的时候眼前一亮,发现VAO和glDrawBuffers都不是WebGL1.0的标准函数,都 ...

  7. Cesium原理篇:6 Render模块(6: Instance实例化)【转】

    https://www.cnblogs.com/fuckgiser/p/6027520.html 最近研究Cesium的实例化,尽管该技术需要在WebGL2.0,也就是OpenGL ES3.0才支持. ...

  8. Cesium原理篇:6 Render模块(3: Shader)

    在介绍Renderer的第一篇,我就提到WebGL1.0对应的是OpenGL ES2.0,也就是可编程渲染管线.之所以单独强调这一点,算是为本篇埋下一个伏笔.通过前两篇,我们介绍了VBO和Textur ...

  9. Cesium原理篇:6 Render模块(5: VAO&RenderState&Command)

    VAO VAO(Vertext Array Object),中文是顶点数组对象.之前在<Buffer>一文中,我们介绍了Cesium如何创建VBO的过程,而VAO可以简单的认为是基于VBO ...

随机推荐

  1. vue.js 的学习

    官方网站(作者居然是中国人) http://vuejs.org/ Vuex中文手册 http://vuex.vuejs.org Vue-Router 手册 http://router.vuejs.or ...

  2. H5 表单

    伴随着互联网富应用以及移动开发的兴起,传统的Web表单已经越来越不能满足开发的需求,HTML5在Web表单方向也做了很大的改进,如拾色器.日期/时间组件等,使表单处理更加高效. 1.1新增表单类型 - ...

  3. PHP基础知识之变量

    定义: 变量用一个美元符号后面跟变量名来表示,如:$user 变量引用赋值: 引用赋值用一个&后面跟源变量名来表示,如:$copy=&$user(注:$bar = &(24 * ...

  4. ExtJS扩展:扩展grid

    ExtJs的grid功能很强大,但是有时候觉得总是少那么一点点功能,我们就来扩展它,让它用起来更方便. 今天我们要扩展的是:根据记录的选择数量来禁用或启用grid toolbar上的某些按钮. 本文所 ...

  5. DBImport v3.44 中文版发布:数据库数据互导及文档生成工具(IT人员必备)

    前言: 距离上一个版本V3.3版本的文章发布,已经是1年10个月前的事了. 其实版本一直在更新,但也没什么大的功能更新,总体比较稳定,所以也不怎么写文介绍了. 至于工作上的事,之前有半年时间跑去学英语 ...

  6. .Net 跨平台可移植类库PCL可用于任何平台包括Mono

    Microsoft 在 .NET Framework 4 中添加了一个名为可移植类库 (PCL) 的新功能. 利用 PCL,您可以有选择性地面向 .NET Framework.Silverlight ...

  7. C++高精度计时器——微秒级时间统计

    在C++中,经常需要通过计时来统计性能信息,通过统计的耗时信息,来分析性能瓶颈,通常情况下,可能毫秒级别的时间统计就足够用了,但是在毫厘必争的性能热点的地方,毫秒级别的统计还是不够的,这种情况下,就需 ...

  8. 纯命令行的编辑利器:用好 awk 与 sed

    awk 的基本语法 awk 是模式查找与处理语言,是文本处理的利器.使用 awk 可以只用一条简单的命令完成复杂的文本数据处理. awk 命令的基本结构为: awk '模式 {处理}' 文件 比如,打 ...

  9. AutoMapper 最佳实践

    AutoMapper 是一个基于命名约定的对象->对象映射工具. 只要2个对象的属性具有相同名字(或者符合它规定的命名约定),AutoMapper就可以替我们自动在2个对象间进行属性值的映射.如 ...

  10. js浏览器对象模型-BOM

    bom browse object model 浏览器对象模型. 也就是window对象下面的东西. location 对象 window.location.href 表示打开窗口的路径. windo ...