一、从构造函数入手
new Cesium.Viewer()是Cesium一切的起点,我们从该构造函数开始,看看Cesium绘制一帧的大概流程,打开Viewer.js源码:
我们可以看到在Viewer类的构造函数中,通过我们传递进来的参数,进行了一些初始化,其中最核心的是:
// Cesium widget
const cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {
baseLayer:
createBaseLayerPicker ||
defined(options.baseLayer) ||
defined(options.imageryProvider)
? false
: undefined,
clock: clock,
skyBox: options.skyBox,
skyAtmosphere: options.skyAtmosphere,
sceneMode: options.sceneMode,
mapProjection: options.mapProjection,
globe: options.globe,
orderIndependentTranslucency: options.orderIndependentTranslucency,
contextOptions: options.contextOptions,
useDefaultRenderLoop: options.useDefaultRenderLoop,
targetFrameRate: options.targetFrameRate,
showRenderLoopErrors: options.showRenderLoopErrors,
useBrowserRecommendedResolution: options.useBrowserRecommendedResolution,
creditContainer: defined(options.creditContainer)
? options.creditContainer
: bottomContainer,
creditViewport: options.creditViewport,
scene3DOnly: scene3DOnly,
shadows: options.shadows,
terrainShadows: options.terrainShadows,
mapMode2D: options.mapMode2D,
blurActiveElementOnCanvasFocus: options.blurActiveElementOnCanvasFocus,
requestRenderMode: options.requestRenderMode,
maximumRenderTimeChange: options.maximumRenderTimeChange,
depthPlaneEllipsoidOffset: options.depthPlaneEllipsoidOffset,
msaaSamples: options.msaaSamples,
});
通过传递到Viewer中的参数,构造了一个CesiumWiget对象。
二、CesiumWiget.render
打开CesiumWidget.js源码:
此类中创建了scene对象
注意我们发现使用了默认的循环渲染,我们看看该属性的定义
this._useDefaultRenderLoop = undefined;
this.useDefaultRenderLoop = defaultValue(
options.useDefaultRenderLoop,
true
);
以看到在startRenderLoop函数内调用requestAnimationFrame函数进行不断地循环渲染,我们所说的绘制一帧,就是requestAnimationFrame函数执行一次的过程。在requestAnimationFrame函数内部,核心代码为:
1、widget.resize();
2、widget.render();
widget.resize方法:主要作用是根据当前窗口的宽高,更新canvas大小,然后更新相机和视口大小。
/**
* Updates the canvas size, camera aspect ratio, and viewport size.
* This function is called automatically as needed unless
* <code>useDefaultRenderLoop</code> is set to false.
*/
CesiumWidget.prototype.resize = function () {
const canvas = this._canvas;
if (
!this._forceResize &&
this._canvasClientWidth === canvas.clientWidth &&
this._canvasClientHeight === canvas.clientHeight &&
this._lastDevicePixelRatio === window.devicePixelRatio
) {
return;
}
this._forceResize = false;
configureCanvasSize(this);
configureCameraFrustum(this);
this._scene.requestRender();
};
widget.render方法:里面主要是调用scene对象的方法,首先是调用scene.initializeFrame初始化帧的状态,然后调用scene.render方法并传入当前时间进行渲染。
/**
* Renders the scene. This function is called automatically
* unless <code>useDefaultRenderLoop</code> is set to false;
*/
CesiumWidget.prototype.render = function () {
if (this._canRender) {
this._scene.initializeFrame();
const currentTime = this._clock.tick();
this._scene.render(currentTime);
} else {
this._clock.tick();
}
};
绘制流程图谱:
三、剖析Scene.render()
打开Scene.js源码:
Scene类构造函数中的重要初始化操作,
1、创建Context绘图上下文对象,使用原生WebGL绘图,您绘先获取一个绘图上下文对象,而此处的绘图上下文,就是类似WebGL绘图上下文。
const context = new Context(canvas, contextOptions);
2、创建FrameState这状态对象,帧状态对象保存了当前一帧的相关信息。
this._frameState = new FrameState(
context,
new CreditDisplay(creditContainer, " • ", creditViewport),
this._jobScheduler
);
3、创建存储Primitive的集合(包含贴地类型)
this