稀土掘金 稀土掘金

Cesium源码跟读之CesiumWidget的实现

这是我参与更文挑战的第2天,活动详情查看: 更文挑战。

前言

没错,我也来读源码了,因为之前的文章中简单提到了一下Cesium的渲染机制,所以我索性一咬牙将这块的源码通读一遍,自己也能更深入的了解Cesium,这篇文章也就作为我的一个记录。准备工作只有一步:从GitHub上拉取一份Cesium最新的代码,找到Source\Widgets\CesiumWidget\CesiumWidget.js文件。

文章中代码仅为涉及到讲解部分的代码,未全部放出

用法

CesiumWidget的用法和Viewer的用法类似,都是通过一个id来生成容器。

const widget = new Cesium.CesiumWidget('id');
const viewer = new Cesium.Viewer('id');

两者的区别是,只要实例化了Viewer则必定会实例化一个CesiumWidget。Viewer除了可视区域外还囊括了各种控件,比如时间轴、时间拨盘、搜索框等。

Cesium中使用WebGL进行绘图的是实例化的Scene模块。CesiumWidget更像一个桥梁,把构造时传递的DOM元素(或ID选择器)再内嵌了一个canvas元素,再将此canvas元素传递给Scene让其绘图。

内部方法

  • [24行]getDefaultSkyBoxUrl:获取默认天空盒资源地址
  • [30行]startRenderLoop:开启渲染循环,这也是我们上一篇中提到的Cesium的渲染机制。利用requestAnimationFrame来不停的刷新渲染页面。
  • [71行]configurePixelRatio:配置像素比。
  • [86行]configureCanvasSize:配置画布大小。
  • [105行]configureCameraFrustum:配置相机视锥。

构造

DOM构造

function CesiumWidget(container, options) {
    if (!defined(container)) {
    	throw new DeveloperError("container is required.");
    }
    
  	container = getElement(container);
  	options = defaultValue(options, defaultValue.EMPTY_OBJECT);
    //Configure the widget DOM elements
    var element = document.createElement("div");
    element.className = "cesium-widget";
    container.appendChild(element);
    var canvas = document.createElement("canvas");
    
    // ……
    
  	element.appendChild(canvas);
}

这部分代码完成了Viewer实例的DOM元素判断、本级DOM元素创建和下级DOM(canvas)的创建,以及传入的options是否为空。

如此DOM的层级便生成完毕了,下方代码(227~263行)主要是像商标这种部分以及分辨率等设置、将CesiumWidget的私有变量赋值并调用configureCanvasSize来修改canvas的大小。

场景构造

从265行开始我们会看到一个巨大的try/catch模块。这部分代码完成了SceneGlobeSkyBoxSkyAtmosphere四个模块的实例化。

这里我们会发现一个问题,在使用CesiumWidget的时候,明明有像CameraImageryLayers之类的API,是从哪来的呢。这里先卖个关子,我们先专注于Scene

var scene = new Scene({
    canvas: canvas,
    contextOptions: options.contextOptions,
    creditContainer: innerCreditContainer,
    creditViewport: creditViewport,
    mapProjection: options.mapProjection,
    orderIndependentTranslucency: options.orderIndependentTranslucency,
    scene3DOnly: defaultValue(options.scene3DOnly, false),
    terrainExaggeration: options.terrainExaggeration,
    shadows: options.shadows,
    mapMode2D: options.mapMode2D,
    requestRenderMode: options.requestRenderMode,
    maximumRenderTimeChange: options.maximumRenderTimeChange,
});
this._scene = scene;

实例化Scene对象,传递构造的参数(绝大部分来自于options)。

scene.camera.constrainedAxis = Cartesian3.UNIT_Z;

configurePixelRatio(this);
configureCameraFrustum(this);

指定camera的约束轴为Z轴,调用函数来配置像素比、相机视锥体。

var ellipsoid = defaultValue(
    scene.mapProjection.ellipsoid,
    Ellipsoid.WGS84
);
var globe = options.globe;
if (!defined(globe)) {
    globe = new Globe(ellipsoid);
}
if (globe !== false) {
    scene.globe = globe;
    scene.globe.shadows = defaultValue(
        options.terrainShadows,
        ShadowMode.RECEIVE_ONLY
    );
}

创建ellipsoidglobe并传递给scene

var skyBox = options.skyBox;
if (!defined(skyBox)) {
    skyBox = new SkyBox({
        sources: {
            positiveX: getDefaultSkyBoxUrl("px"),
            negativeX: getDefaultSkyBoxUrl("mx"),
            positiveY: getDefaultSkyBoxUrl("py"),
            negativeY: getDefaultSkyBoxUrl("my"),
            positiveZ: getDefaultSkyBoxUrl("pz"),
            negativeZ: getDefaultSkyBoxUrl("mz"),
        },
    });
}
if (skyBox !== false) {
    scene.skyBox = skyBox;
    scene.sun = new Sun();
    scene.moon = new Moon();
}

// Blue sky, and the glow around the Earth's limb.
var skyAtmosphere = options.skyAtmosphere;
if (!defined(skyAtmosphere)) {
    skyAtmosphere = new SkyAtmosphere(ellipsoid);
}
if (skyAtmosphere !== false) {
    scene.skyAtmosphere = skyAtmosphere;
}

创建天空盒、大气环境等环境因素。

//Set the base imagery layer
var imageryProvider =
    options.globe === false ? false : options.imageryProvider;
if (!defined(imageryProvider)) {
    imageryProvider = createWorldImagery();
}

if (imageryProvider !== false) {
    scene.imageryLayers.addImageryProvider(imageryProvider);
}

//Set the terrain provider if one is provided.
if (defined(options.terrainProvider) && options.globe !== false) {
    scene.terrainProvider = options.terrainProvider;
}

设置影像数据源,如果没有配置,则调用createWorldImagery模块创建世界影像并传递给scene,如果配置中有地形数据源,则将其传递给scene

this._screenSpaceEventHandler = new ScreenSpaceEventHandler(canvas);

创建事件处理器,用于下方暴露在原型中。

if (defined(options.sceneMode)) {    if (options.sceneMode === SceneMode.SCENE2D) {        this._scene.morphTo2D(0);    }    if (options.sceneMode === SceneMode.COLUMBUS_VIEW) {        this._scene.morphToColumbusView(0);    }}

判断scene的视图模式是二维、三维还是2.5的。

var that = this;this._onRenderError = function (scene, error) {    that._useDefaultRenderLoop = false;    that._renderLoopRunning = false;    if (that._showRenderLoopErrors) {        var title =            "An error occurred while rendering.  Rendering has stopped.";        that.showErrorPanel(title, undefined, error);    }};scene.renderError.addEventListener(this._onRenderError);

scene绑定了渲染错误事件的处理函数。

this._useDefaultRenderLoop = undefined;this.useDefaultRenderLoop = defaultValue(    options.useDefaultRenderLoop,    true);this._targetFrameRate = undefined;this.targetFrameRate = options.targetFrameRate;

确认是否使用默认循环机制,如果配置为false则需要手动调用render()方法,否则使用目标帧速率进行渲染。

原型定义

我们在上边提过,CesiumWidget那么多的API都是从哪来的,代码读到这里我们就会发现,从390行向下,都是利用Object.defineProperties()在CesiumWidget的原型上添加属性以及方法。

属性定义
Object.defineProperties(CesiumWidget.prototype, {    container: {        get: function () {            return this._container;        },    },    canvas: {        get: function () {            return this._canvas;        },    },    // ……}
方法定义
  • [640行] CesiumWidget.prototype.showErrorPanel:向用户显示错误面板,其中包含标题和较长的错误消息,可以使用’确定’按钮将其关闭。该面板自动显示发生渲染循环错误时,如果showRenderLoopErrors不为false,则当小部件已构建。
  • [740行]CesiumWidget.prototype.isDestroyed:返回CesiumWidget是否被销毁。
  • [748行]CesiumWidget.prototype.destroy:销毁视图。
  • [763行]CesiumWidget.prototype.resize:更新画布大小、相机纵横比和视口大小。这个函数默认会自动调用,除非useDefaultRenderLoop设置为false
  • [785行]CesiumWidget.prototype.render:渲染场景,同样的它也会自动调用,除非useDefaultRenderLoop设置为false

导出

export default CesiumWidget;

最后对整个模块进行导出。

结构图

image-20210531155553870.png

最后

我们只是针对这个文件中的私有函数以及构造函数进行阅读和分析,并未对其引用的模块比如SceneGlobe等进行阅读。感兴趣的小伙伴可以针对其中一个引入的模块进行更深入的阅读。如果是想在Cesium上深入研究的话我认为有些功能和实现的源码大概通读一下,了解一下实现原理是有必要的。

玻璃钢生产厂家鸡西玻璃钢雕塑制作厂家玻璃钢仿铜雕塑培训学校广安玻璃钢海豚雕塑定制吕梁玻璃钢仿铜雕塑白山卡通玻璃钢雕塑小瓶酒美陈图片商场 效果图苏州玻璃钢花盆厂家玻璃钢雕塑怎样上色红色玻璃钢人物雕塑批量定制乌鲁木齐玻璃钢雕塑安装工四会玻璃钢雕塑厂漳州玻璃钢雕塑工厂张掖玻璃钢植物雕塑公司惠州定制玻璃钢面包雕塑重庆商场美陈装饰设计福建卡通玻璃钢雕塑景观装饰重庆玻璃钢雕塑天壶需多少钱玻璃钢鼠年雕塑主题玻璃钢雕塑定制德州花朵玻璃钢仿铜雕塑公司广东玻璃钢雕塑批发江苏室内商场美陈制造河南喷泉雕塑玻璃钢玻璃钢名人雕塑订做商场直梯美陈夏邑制作玻璃钢雕塑产品价格淮南玻璃钢仿真雕塑加工厂上海季节性商场美陈批发临江玻璃钢人物雕塑上海玻璃钢卡通雕塑阿狸香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化