Cesium 源码分析入门(一)

一、现成的源码调试文件

源码目录结构详解中已经介绍过,Apps文件夹下的文件是基于 Source 目录下的源码直接创建出来的现成的页面程序。所以,可以直接使用Apps里的示例进行ceisum源码调试。
例如,CesiumViewer示例,Apps\CesiumViewer\index.html为入口文件,运行结果如下图所示。
页面展示

二、加载地球

为了更简单的学习,简化了Apps\CesiumViewer\index.html和Apps\CesiumViewer\CesiumViewer.js两个文件,实现调用源码去加载地球的功能。如果源码部署在了IIS服务器上,修改代码后直接在浏览器刷新或强制刷新,则即时生效。
Apps\CesiumViewer\index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
    <title>Cesium Viewer</title>
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="CesiumViewer.css" media="screen" />
  </head>
  <body style="background: #000">
  	<-- 定义地图容器 -->
    <div id="cesiumContainer" class="fullWindow"></div>
    <-- 引入脚本文件 -->
    <script src="CesiumViewer.js" type="module"></script>
  </body>
</html>

在源码项目构建打包(运行 build 指令)之后,会出现Source 目录的入口索引文件,文件名是 Cesium.js,以供按需使用 Source源码目录下面对应的功能模块。
Apps\CesiumViewer\CesiumViewer.js
如果是加载地球和默认的基础部件的话,需调用Viewer 功能模块,如下所示。

window.CESIUM_BASE_URL = "../../Source/";
import { Viewer } from "../../Source/Cesium.js";
function main() {
  // 最高端的食材,往往只需要最简单的烹饪。加载一个cesium地球,仅仅只需要一行代码。
  let viewer = new Viewer("cesiumContainer");
}
main();

如果只是加载一个干净的地球,不加载地图部件儿,只需调用CesiumWidget功能模块,如下所示。

window.CESIUM_BASE_URL = "../../Source/";
import { CesiumWidget } from "../../Source/Cesium.js";
function main() {
  const cesiumWidget = new CesiumWidget(document.getElementById("cesiumContainer"));
}
main();

三、源码分析——Viewer.js

从Cesium.js文件中,可以找到Viewer功能模块代码位于./Widgets/Viewer/Viewer.js

export { default as Viewer } from './Widgets/Viewer/Viewer.js';

打开Viewer.js文件后发现有两千多行代码,不要慌,一步一步来,直接Ctrl+F搜索 function Viewer进行定位。仔细研读之后,发现Viewer也是一个地球的壳子。在此先简单解读一下Viewer.js脚本。
大致过程就是:

  • 获取dom容器 →
  • 判断options参数,若不存在则 freeze 冻结赋值空对象 →
  • 使用CesiumWidget创建地球 →
  • 初始化地图小部件(Selection Indicator、Info Box、Main Toolbar、Geocoder、HomeButton、SceneModePicker、BaseLayerPicker、Navigation Help Button、Animation、Timeline、Fullscreen、VR等等) →
  • 最后,将以上初始化的对象,全部注册注册为当前Viewer实例的属性,并将其中一些对象例如dataSourceCollection等公共函数API和私有函数API一并注册到Viewer的原型链上(Object.defineProperties)附上Ceisum API地址
  • 另外Cesium还默认为cesiumWidget注册了屏幕操作事件的点击、双击事件,方便初始化完成后能通过点击来拾取场景中的Entity

需要注意,使用CesiumWidget创建地球之前,源码中还有很多判断与赋值,这是因为 使用cesiumWidget方法创建地球的options参数中,有几项参数是根据new Viewer(container,options)中传入的container和options参数来进行二次判断筛选来进行赋值的,主要包括:
判断是否使用BaseLayerPicker部件 → 嵌套div,创建地图div容器和版权div容器 → 获取时钟模型 → 判断是否仅使用3d场景,

所以,官方的代码写的逻辑,一定要仔细去理解,不要被乱象迷失 而 轻言放弃!

function Viewer(container, options) {
	// 判断是否传入domid,如果没有传值,则抛出错误,defined方法:判断参数是否为undifined或null,如果不是则返回true。
	if (!defined(container)) {
	    throw new DeveloperError("container is required.");
	}
	
	// 根据domid获取DOM元素,使用getElement方法返回dom元素【document.getElementById()】。
	container = getElement(container);
	
	// 判断是否传入options,如果为空,则使用预设值:defaultValue.EMPTY_OBJECT:{}
	// defaultValue方法:判断第一个参数如果不存在,则把第二个参数作为它的值返回,如果存在,那就返回它本身。
	options = defaultValue(options, defaultValue.EMPTY_OBJECT);

	// 判断options是否存在globe属性、baseLayerPicker属性,且判断值不为false
	// 如果都不存在,则createBaseLayerPicker为true,即使用BaseLayerPicker控件
	const createBaseLayerPicker = (!defined(options.globe) || options.globe !== false) && (!defined(options.baseLayerPicker) || options.baseLayerPicker !== false);
	
	// 如果不使用BaseLayerPicker控件,但是指定了selectedImageryProviderViewModel参数,则抛出无效参数的错误
  	if (!createBaseLayerPicker && defined(options.selectedImageryProviderViewModel)) {
	    throw new DeveloperError("options.selectedImageryProviderViewModel is not available when not using the BaseLayerPicker widget. Either specify options.imageryProvider instead or set options.baseLayerPicker to true.");
	}
	// 如果不使用BaseLayerPicker控件,但是指定了selectedTerrainProviderViewModel参数,则抛出无效参数的错误
	if (!createBaseLayerPicker && defined(options.selectedTerrainProviderViewModel)) {
	    throw new DeveloperError("options.selectedTerrainProviderViewModel is not available when not using the BaseLayerPicker widget. Either specify options.terrainProvider instead or set options.baseLayerPicker to true.");
	}
	// 为了避免有this指向错误问题,定义了that属性
	const that = this;
	
	// 在我们创建的id为cesiumContainer容器div里面嵌套一个class为cesium-viewer的div
	const viewerContainer = document.createElement("div");
	viewerContainer.className = "cesium-viewer";
	container.appendChild(viewerContainer);
	
	// 在class为cesium-viewer的div里再嵌套一个class为cesium-viewer-cesiumWidgetContainer的div,
	// 专门存放地球的div容器,作为Cesium widget container
	const cesiumWidgetContainer = document.createElement("div");
	cesiumWidgetContainer.className = "cesium-viewer-cesiumWidgetContainer";
	viewerContainer.appendChild(cesiumWidgetContainer);
	
	// 在class为cesium-viewer的div里再嵌套一个class为cesium-viewer-bottom的div,用于显示地球底部的版权注释信息
	const bottomContainer = document.createElement("div");
	bottomContainer.className = "cesium-viewer-bottom";
	viewerContainer.appendChild(bottomContainer);
	
	// 判断options.scene3DOnly参数是否存在,如果没有则默认为false,即是否仅使用3d场景
	const scene3DOnly = defaultValue(options.scene3DOnly, false);
	
  	let clockViewModel;
  	let clock;
  	let destroyClockViewModel = false;
  	
  	// 判断options.clockViewModel是否存在,若存在则使用传入的时钟模型,否则使用系统默认的时钟模型
  	if (defined(options.clockViewModel)) {
    	clockViewModel = options.clockViewModel;
    	clock = clockViewModel.clock;
  	} else {
    	clock = new Clock();
    	clockViewModel = new ClockViewModel(clock);
    	destroyClockViewModel = true;
  	}
	// 判断options.shouldAnimate是否存在,若存在则将时钟的同名属性设为同样的值。
  	if (defined(options.shouldAnimate)) {
    	clock.shouldAnimate = options.shouldAnimate;
  	}
  	
  	// 调用CesiumWidget模块,创建地球
	const cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {
	  	imageryProvider:
      createBaseLayerPicker || defined(options.imageryProvider)
        ? false
        : undefined,
	    clock: clock,
	    creditContainer: defined(options.creditContainer)
	      ? options.creditContainer
	      : bottomContainer,
	    scene3DOnly: scene3DOnly,
	    //......省略一些options属性值
	});
	//最后,将以上初始化的对象,全部注册注册为当前Viewer实例的属性,并将其中一些对象例如dataSourceCollection的一些事件一并注册到Viewer的原型上。
	// 另外Cesium还默认为cesiumWidget注册了屏幕操作事件的点击、双击事件,方便初始化完成后能通过点击来拾取场景中的Entity
}

参考文章: Cesium 源码笔记[1] Viewer模块实例化的大致过程 ver1.67

  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cesium 是一个基于 WebGL 的开地球渲染引擎,主要用于在浏览器中呈现三维地理数据。它使用了一些复杂的渲染技术和算法来实现高性能和高质量的地球渲染效果。下面是对 Cesium 渲染码的简要分析: 1. Shader:Cesium 使用了大量的着色器程序来实现不同的渲染效果,如光照、纹理映射、阴影等。着色器程序是在 GPU 上执行的,并且可以通过 GLSL(OpenGL Shading Language)语言进行编写。Cesium 的着色器程序通常是在运行时动态生成的,以适应不同的地图数据和渲染需求。 2. 地形渲染:Cesium 支持高度精细的地形渲染,它使用了基于切片的地形渲染技术。在渲染过程中,Cesium 会将地球表面分割成小块切片,并根据每个切片的高度数据和纹理信息生成相应的网格和纹理。这些切片可以根据需要进行加载和卸载,以实现地图数据的动态加载和显示。 3. 纹理映射:Cesium 使用了纹理映射技术来实现地球表面的贴图效果。它可以将不同类型的纹理(如卫星图像、地形纹理、气候数据等)映射到地球表面的不同部分,以实现真实的地貌效果。Cesium 通过加载和解析各种类型的地图数据,将其转换为纹理信息,并将其应用到地球表面的相应区域。 4. 光照和阴影:Cesium 支持实时的光照和阴影效果,以增强地球渲染的真实感。它使用了基于物理的光照模型,考虑了光的位置、光照强度、表面材质等因素,并通过计算每个顶点和像素的光照值来实现逼真的光照效果。此外,Cesium 还支持动态阴影的生成,可以根据光的位置和地形的形状计算出地球表面上的阴影效果。 5. 动态渲染:Cesium 支持动态渲染技术,可以在实时交互的情况下实现高性能的地球渲染效果。它使用了一些优化技术,如级联阴影映射、视锥剔除、LOD(Level of Detail)等,以减少渲染负载并提高渲染效率。此外,Cesium 还支持动态加载和卸载地图数据,以实现在不同的视角和缩放级别下的快速渲染和响应。 需要注意的是,Cesium码非常庞大和复杂,涉及到多个模块和子系统。以上只是对其渲染部分的简要分析,实际的分析需要深入研究 Cesium 的代码库和文档,并对 WebGL 和图形渲染技术有一定的了解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值