cesium源代码解析一三维场景是如何渲染的

寻医问药

  • 你想了解cesium的源码吗?
  • 你是不是在庞大的源码中迷失过方向?
  • cesium是如何一步步的把场景渲染出来的?

直奔主题

1、万物起源----时间

//Clock.js
Clock.prototype.tick = function () {
  var currentSystemTime = getTimestamp();
  var currentTime = JulianDate.clone(this._currentTime);
  ...//省略
  this._currentTime = currentTime;
  this._lastSystemTime = currentSystemTime;
  this.onTick.raiseEvent(this);
  return currentTime;
};

说明:通过代码可以看出当tick函数执行的时候会同步执行onTick中被订阅的动作。

2、渲染频率----帧率

//requestAnimationFrame.js
(function () {
  // look for vendor prefixed function
  if (!defined(implementation) && typeof window !== "undefined") {
    var vendors = ["webkit", "moz", "ms", "o"];
    var i = 0;
    var len = vendors.length;
    while (i < len && !defined(implementation)) {
      implementation = window[vendors[i] + "RequestAnimationFrame"];
      ++i;
    }
  }
  // build an implementation based on setTimeout
  if (!defined(implementation)) {
    var msPerFrame = 1000.0 / 60.0;
    var lastFrameTime = 0;
    implementation = function (callback) {
      var currentTime = getTimestamp();

      // schedule the callback to target 60fps, 16.7ms per frame,
      // accounting for the time taken by the callback
      var delay = Math.max(msPerFrame - (currentTime - lastFrameTime), 0);
      lastFrameTime = currentTime + delay;

      return setTimeout(function () {
        callback(lastFrameTime);
      }, delay);
    };
  }
})();

说明:通过代码可以看出cesium对各种浏览器的requestAnimationFrame函数做了兼容处理。

3、渲染----场景

Scene.prototype.render = function (time) {
  this._preUpdate.raiseEvent(this, time);

  var frameState = this._frameState;
  frameState.newFrame = false;
  ...//省略
  this._postUpdate.raiseEvent(this, time);

  if (shouldRender) {
    this._preRender.raiseEvent(this, time);
    frameState.creditDisplay.beginFrame();
    tryAndCatchError(this, render);
  }
  if (shouldRender) {
    this._postRender.raiseEvent(this, time);
    frameState.creditDisplay.endFrame();
  }
};

说明:通过代码可以看出这个函数负责更新场景所有图元数据的状态,进而由数据自己根据状态决定是否渲染,顺便也看清楚了preRender、postUpdate这俩个重要的回调事件的触发机制。

调用过程

  • 通过上面三个源代码片段已经可以看出整个调用流程了,下面我们一步步接开代码的执行逻辑
  • 1、初始化场景
  var  viewer = new Viewer("cesiumContainer", {
     imageryProvider: imageryProvider,
     baseLayerPicker: hasBaseLayerPicker,
     scene3DOnly: endUserOptions.scene3DOnly,
     requestRenderMode: true,
   });
  • 2、new Viewer()做了什么?
function Viewer(container, options) {
...//省略
// Cesium widget
var cesiumWidget = new CesiumWidget();
 ...//省略
}
  • 3、可以看出它核心是初始化了CesiumWidget,那么new CesiumWidget()又做了什么?
function CesiumWidget(container, options) {
 ...//省略
  var scene = new Scene();
  this._scene = scene;
  this._screenSpaceEventHandler = new ScreenSpaceEventHandler(canvas);
  this._useDefaultRenderLoop = undefined;
  this.useDefaultRenderLoop = defaultValue(
    options.useDefaultRenderLoop,
    true
  );
   ...//省略
}
  • 4、可以看出它又new Scene()对象,然后把useDefaultRenderLoop属性设置为true。这个简单的一句话很重要,因为它是整个调用循环的开始一环,这个操作会触发下面一段代码
useDefaultRenderLoop: {
  get: function () {
    return this._useDefaultRenderLoop;
  },
  set: function (value) {
    if (this._useDefaultRenderLoop !== value) {
      this._useDefaultRenderLoop = value;
      if (value && !this._renderLoopRunning) {
        startRenderLoop(this);
      }
    }
  },
},
}
  • 5、startRenderLoop函数代码如下
function startRenderLoop(widget) {
  widget._renderLoopRunning = true;
  var lastFrameTime = 0;
  function render(frameTime) {
     ...//省略
     widget.resize();
     widget.render();
     requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
  • 6、从这段代码可以看出它调用了文章开头我们说的requestAnimationFrame函数,进而周而复始的执行widget.render()函数,其代码如下
CesiumWidget.prototype.render = function () {
  if (this._canRender) {
    this._scene.initializeFrame();
    var currentTime = this._clock.tick();
    this._scene.render(currentTime);
  } else {
    this._clock.tick();
  }
};
  • 7、从这段代码可以看出它调用了scene.render()函数进而把整个场景图元渲染出来,还调用了clock.tick()函数,从而执行onTick所有被订阅的函数。

复盘----总结梳理

  1. 先初始化Viewer对象
  2. 再初始化CesiumWidget对象
  3. 通过useDefaultRenderLoop属性触发requestAnimationFrame函数
  4. 通过requestAnimationFrame函数触发widget.render()函数
  5. 通过widget.render()函数触发scene.render和clock.tick()函数
  6. 完成后,周而复始4、5俩步

为了便于大家理解,我省略了本次渲染逻辑无关的相关代码,所以没有省略的代码都是非常重要的代码,建议大家对着这篇文章和源代码仔细认真阅读

更多----加入我们

  1. 这里有个地方需要说明,由于篇幅和时间有限,没有写出所有代码,这里主要列出来了主要实现思路
  2. 如果你还有不了解的地方
  3. 如果你还需要进行cesium交流
  4. 那一起学习探讨吧
  5. 你可以加入我们的基地,我们基地的地址是:450342630(QQ群号)
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值