对于地形处理的过程的大概认识是,渲染需要地形数据,地形数据的下载需要依据相机的位置确定, 地形数据的下载需要地图服务,而地图服务也有很多种(WMS(网络地图服务)、WMTS(网络地图瓦片服务)、TMS(瓦片地图服务)),针对TMS服务输出的地形瓦片数据也有很多种格式(quantized-mesh、heightmap等) ,gis行业多年以来出现的各种技术、规范构成今天庞大的gis行业体系。
Cesium中的地球被封装在了Globe类中,通过这个中将各个功能进行拆分成几个模块用于地形的加载、影像的加载、材质的处理等过程。
本文只是大概讲解以下流程,具体过程分几节解析,下面是个草图,后续会完善。
讲解流程分三条主线
1、Globe的创建过程 :
a、在CesiumWidget.js这个类中创建了Globe对象,这个类的创建时传入了一个椭球,默认是Ellipsoid.WGS84椭球;
b、在Globe对象内部,封装了相当多的成员变量,其中比较重量级的是GlobeSurfaceShaderSet、QuadtreePrimitive、GlobeSurfaceTileProvider、EllipsoidTerrainProvider、ImageryLayerCollection;
GlobeSurfaceShaderSet:用于地球材质的封装;
QuadtreePrimitive: 四叉树的封装;
GlobeSurfaceTileProvider:封装了地球瓦片的创建、瓦片中地形数据的组织、瓦片中 影像数据的组织等;
EllipsoidTerrainProvider:地形数据的范围、规格、网格数据的解码、gpu资源的创建 等过程;
ImageryLayerCollection:这是一个集合,因为地球可以叠加多个影像层。
function Globe(ellipsoid) {
// 椭球规格
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
// 地形(请求地形数据),默认使用高度图
var terrainProvider = new EllipsoidTerrainProvider({
ellipsoid: ellipsoid,
});
// 影像集合,可以同时使用多个影像(请求影像数据)
var imageryLayerCollection = new ImageryLayerCollection();
// 椭球
this._ellipsoid = ellipsoid;
// 影像集合
this._imageryLayerCollection = imageryLayerCollection;
// 地球表面shader集合
this._surfaceShaderSet = new GlobeSurfaceShaderSet();
// 材质
this._material = undefined;
// 四叉树图元地球表面
this._surface = new QuadtreePrimitive({
// 地球表面瓦片提供商(封装了地形、影像提供商)
tileProvider: new GlobeSurfaceTileProvider({
terrainProvider: terrainProvider, // 地形提供商(数据)
imageryLayers: imageryLayerCollection, // 影像提供商(数据)
surfaceShaderSet: this._surfaceShaderSet, // 着色器集合(着色)
}),
});
// 地形提供商
this._terrainProvider = terrainProvider;
// 地形提供商改变了
this._terrainProviderChanged = new Event();
// 颜色
this._undergroundColor = Color.clone(Color.BLACK);
// 地下面的颜色 透明度随着距离变化
this._undergroundColorAlphaByDistance = new NearFarScalar(
ellipsoid.maximumRadius / 1000.0,
0.0,
ellipsoid.maximumRadius / 5.0,
1.0
);
// 封装了地球透明属性
this._translucency = new GlobeTranslucency();
// 着色器脏了
makeShadersDirty(this);
/**
* Determines if the globe will be shown.
* 地球显示
*
* @type {Boolean}
* @default true
*/
this.show = false;
// 海洋法线图资源脏了
this._oceanNormalMapResourceDirty = true;
// 海洋法线图片资源
this._oceanNormalMapResource = new Resource({
url: buildModuleUrl("Assets/Textures/waterNormalsSmall.jpg"),
});
/**
* The maximum screen-space error used to drive level-of-detail refinement. Higher
* values will provide better performance but lower visual quality.
*
* 最大屏幕误差用来驱动lod改进,数值大将提高加载效率但是会降低质量
*
* @type {Number}
* @default 2
*/
this.maximumScreenSpaceError = 2;
/**
* The size of the terrain tile cache, expressed as a number of tiles. Any additional
* tiles beyond this number will be freed, as long as they aren't needed for rendering
* this frame. A larger number will consume more memory but will show detail faster
* when, for example, zooming out and then back in.
*
* 地形图块缓存的大小,以图块数表示。任何额外的超过此数字的平铺将被释放,只要渲染不需要这个框架。
* 较大的数字将消耗更多的内存,但会更快地显示细节例如,当放大然后再放大时。
*
* @type {Number}
* @default 100
*/
this.tileCacheSize = 100;
/**
* Gets or sets the number of loading descendant tiles that is considered "too many".
* If a tile has too many loading descendants, that tile will be loaded and rendered before any of
* its descendants are loaded and rendered. This means more feedback for the user that something
* is happening at the cost of a longer overall load time. Setting this to 0 will cause each
* tile level to be loaded successively, significantly increasing load time. Setting it to a large
* number (e.g. 1000) will minimize the number of tiles that are loaded but tend to make
* detail appear all at once after a long wait.
*
* 加载后代瓦片的数量被认为是太多,如果一个瓦片加载了太多的后代瓦片,那么这个瓦片在任何后代瓦片的加载和渲染之前被加载和渲染。
* 这意味着用户可以得到更多的反馈这是以更长的总加载时间为代价的。将此设置为0将导致连续加载瓷砖层,显著增加加载时间。
* 将其设置为大的数量(例如,1000)将使装载的瓷砖数量最小化,但往往会使其变小在长时间等待后,细节会立即出现。
*
* @type {Number}
* @default 20
*/
// 加载后代限制
this.loadingDescendantLimit = 20;
/**
* Gets or sets a value indicating whether the ancestors of rendered tiles should be preloaded.
* Setting this to true optimizes the zoom-out experience and provides more detail in
* newly-exposed areas when panning. The down side is that it requires loading more tiles.
* 这个值指定当前渲染的瓦片的祖先节点是否被预先加载,将此设置为true可优化缩小体验,特别是再向上拉升相机的时候,
* 并在平移时提供新曝光区域的更多细节。缺点是它需要加载更多的瓦片。
* @type {Boolean}
* @default true
*/
// 预先加载祖先(拉升过程中流畅)
this.preloadAncestors = true;
/**
* Gets or sets a value indicating whether the siblings of rendered tiles should be preloaded.
* Setting this to true causes tiles with the same parent as a rendered tile to be loaded, even
* if they are culled. Setting this to true may provide a better panning experience at the
* cost of loading more tiles.
* 获取或设置一个值,该值指示是否应预加载当前瓦片的同级瓦片,当相机在当前级别平移时,可以快速加载。
* 将此设置为true将导致加载与渲染瓦片具有相同父级的瓦片,即使它们被剔除。将此设置为true可以提供更好
* 的平移体验,但代价是加载更多的块。
* @type {Boolean}
* @default false
*/
// 预加载当前瓦片级别的兄弟瓦片,平移时快速显示
this.preloadSiblings = false;
/**
* The color to use to highlight terrain fill tiles. If undefined, fill tiles are not
* highlighted at all. The alpha value is used to alpha blend with the tile's
* actual color. Because terrain fill tiles do not represent the actual terrain surface,
* it may be useful in some applications to indicate visually that they are not to be trusted.
*
* 用于高亮填充地形颜色,如果不设置,瓦片不会高亮,alpha用于混合瓦片的真实颜色,
* 由于地形填充块不代表实际的地形表面,因此在某些应用程序中,视觉上指示它们不可信可能会很有用。
* @type {Color}
* @default undefined
*/
// 填充高亮颜色
this.fillHighlightColor = undefined;
/**
* Enable lighting the globe with the scene's light source.
* 启用地球、场景灯光
*
* @type {Boolean}
* @default false
*/
// 启用光源
this.enableLighting = false;
/**
* Enable dynamic lighting effects on atmosphere and fog. This only takes effect
* when enableLighting
is true
.
* 使用大气和雾的灯光效果
*
* @type {Boolean}
* @default true
*/
// 动态大气光晕,必须启用灯光才有用
this.dynamicAtmosphereLighting = true;
/**
* Whether dynamic atmosphere lighting uses the sun direction instead of the scene's
* light direction. This only takes effect when enableLighting
and
* dynamicAtmosphereLighting
are true
.
* 使用太阳光代替场景中的灯光
*
* @type {Boolean}
* @default false
*/
// 来自太阳的动态大气光晕
this.dynamicAtmosphereLightingFromSun = false;
/**
* Enable the ground atmosphere, which is drawn over the globe when viewed from a distance between lightingFadeInDistance
and lightingFadeOutDistance
.
* 启用地面大气,
* @demo {@link https://sandcastle.cesium.com/index.html?src=Ground%20Atmosphere.html|Ground atmosphere demo in Sandcastle}
*
* @type {Boolean}
* @default true
*/
// 启用地面大气
this.showGroundAtmosphere = true;
/**
* The distance where everything becomes lit. This only takes effect
* when enableLighting
or showGroundAtmosphere
is true
.
*
* @type {Number}
* @default 10000000.0
*/
// 相机距离地球多远灯光不可见
this.lightingFadeOutDistance = 1.0e7;
/**
* The distance where lighting resumes. This only takes effect
* when enableLighting
or showGroundAtmosphere
is true
.
* 照明恢复的距离。这只会生效当 启用照明 或 showGroundAtmosphere 为 真时 。
* @type {Number}
* @default 20000000.0
*/
// 照明恢复的距离
this.lightingFadeInDistance = 2.0e7;
/**
* The distance where the darkness of night from the ground atmosphere fades out to a lit ground atmosphere.
* This only takes effect when showGroundAtmosphere
, enableLighting
, and
* dynamicAtmosphereLighting
are true
.
*
* 夜色从地面大气逐渐变暗到地面大气变亮的距离。这仅在 showGroundAtmosphere 和 DynamicMoSphereLighting 为 真 时生效。
*
* @type {Number}
* @default 10000000.0
*/
this.nightFadeOutDistance = 1.0e7;
/**
* The distance where the darkness of night from the ground atmosphere fades in to an unlit ground atmosphere.
* This only takes effect when showGroundAtmosphere
, enableLighting
, and
* dynamicAtmosphereLighting
are true
.
*
* 夜色从地面大气逐渐变暗到无光地面大气的距离。 这仅在 showGroundAtmosphere 和 启用照明 时生效 dynamicatmospherlighting 为 true。
*
* @type {Number}
* @default 50000000.0
*/
this.nightFadeInDistance = 5.0e7;
/**
* True if an animated wave effect should be shown in areas of the globe
* covered by water; otherwise, false. This property is ignored if the
* terrainProvider
does not provide a water mask.
*
* 如果应在全球区域显示动画波浪效果,则为True被水覆盖;否则,这是错误的。如果terrainProvider不提供水面罩。
*
* @type {Boolean}
* @default true
*/
this.showWaterEffect = true;
/**
* True if primitives such as billboards, polylines, labels, etc. should be depth-tested
* against the terrain surface, or false if such primitives should always be drawn on top
* of terrain unless they're on the opposite side of the globe. The disadvantage of depth
* testing primitives against terrain is that slight numerical noise or terrain level-of-detail
* switched can sometimes make a primitive that should be on the surface disappear underneath it.
*
* 如果应该对广告牌、多段线、标签等基本体进行深度测试,则为True 如果这些基本体始终绘制在顶部,
* 则为假除非他们在地球的另一边。深度的缺点 根据地形测试基本体是指轻微的数值噪声或地形细节级别
* 切换有时会使本应位于曲面上的图元消失在其下方。
*
* @type {Boolean}
* @default false
*
*/
// 是否启用深度测试
this.depthTestAgainstTerrain = false;
/**
* Determines whether the globe casts or receives shadows from light sources. Setting the globe
* to cast shadows may impact performance since the terrain is rendered again from the light's perspective.
* Currently only terrain that is in view casts shadows. By default the globe does not cast shadows.
* 地球是否投射和接收灯光阴影,设置灯光会引起场景渲染的性能问题
* @type {ShadowMode}
* @default ShadowMode.RECEIVE_ONLY
*/
// 阴影
this.shadows = ShadowMode.RECEIVE_ONLY;
/**
* The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A hue shift of 1.0 indicates a complete rotation of the hues available.
* 要应用于大气的色调偏移。默认值为0.0(无移位)。色调偏移1.0表示可用色调的完全旋转。
* @type {Number}
* @default 0.0
*/
// 大气色相
this.atmosphereHueShift = 0.0;
/**
* The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A saturation shift of -1.0 is monochrome.
* 应用于大气的饱和位移。默认值为0.0(无移位)。饱和度偏移-1.0是单色的
* @type {Number}
* @default 0.0
*/
// 大气饱和度
this.atmosphereSaturationShift = 0.0;
/**
* The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A brightness shift of -1.0 is complete darkness, which will let space show through.
* 应用于大气的亮度偏移。默认值为0.0(无移位)。亮度偏移-1.0表示完全黑暗,这将让空间显示出来。
* @type {Number}
* @default 0.0
*/
// 大气亮度
this.atmosphereBrightnessShift = 0.0;
/**
* A scalar used to exaggerate the terrain. Defaults to 1.0
(no exaggeration).
* A value of 2.0
scales the terrain by 2x.
* A value of 0.0
makes the terrain completely flat.
* Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid.
*
* 用于放大地形的标量。默认值为1.0
(不夸张)。2.0的值将地形缩放2倍。0.0值使地形完全平坦。
* 请注意,地形夸张不会修改任何其他基本体,因为它们是相对于椭球体定位的。
*
* @type {Number}
* @default 1.0
*/
// 地形夸张
this.terrainExaggeration = 1.0;
/**
* The height from which terrain is exaggerated. Defaults to 0.0
(scaled relative to ellipsoid surface).
* Terrain that is above this height will scale upwards and terrain that is below this height will scale downwards.
* Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid.
* 地形被夸大的高度。默认值为0.0(相对于椭球面缩放)。高于此高度的地形将向上缩放,低于此高度的地势将向下缩放。
* 请注意,地形夸张不会修改任何其他基本体,因为它们是相对于椭球体定位的。
*
* If {@link Globe#terrainExaggeration} is 1.0
this value will have no effect.
* @type {Number}
* @default 0.0
*/
// 地形夸张(在什么地方作为夸张的起始点,分别向上、向下夸张)
this.terrainExaggerationRelativeHeight = 0.0;
/**
* Whether to show terrain skirts. Terrain skirts are geometry extending downwards from a tile's edges used to hide seams between neighboring tiles.
* Skirts are always hidden when the camera is underground or translucency is enabled.
*是否显示地形裙边。地形裙边是从瓦片边缘向下延伸的几何体,用于隐藏相邻瓦片之间的接缝。
*当相机位于地下或启用半透明时,裙边始终隐藏。
* @type {Boolean}
* @default true
*/
// 显示裙边
this.showSkirts = true;
/**
* Whether to cull back-facing terrain. Back faces are not culled when the camera is underground or translucency is enabled.
* 地形背面剔除,相机在地下或地形透明是不会被剔除
* @type {Boolean}
* @default true
*/
// 是否剔除背面
this.backFaceCulling = true;
// 海洋法线球
this._oceanNormalMap = undefined;
// 缩小海洋高光强度
this._zoomedOutOceanSpecularIntensity = undefined;
}
2、更新过程
a、在Scene.js这个类中会调用Globe的更新方法,scene.globe.update。
update主要是处理影像图层集合,为图层设置编号,更新影像图层的显示还是隐藏状态,当图层由显示变为隐藏是回调用自定义事件。
3、渲染过程
a、在Scene.js这个类中会调用Globe的方法,整个流程就开始启动了;
b、Scene中关于Globe的方法在render方法中,包括了scene.globe.beginFrame、 scene._globe.render、scene.globe.endFrame;
beginFrame:用于处理水面的法线、下载策略的一些标记、着色器中uniform数据的 传递、初始化地球表面的一些状态;
// 开始一帧时的操作
Globe.prototype.beginFrame = function (frameState) {
// 地球表面
var surface = this._surface;
// 地球表面瓦片提供商
var tileProvider = surface.tileProvider;
// 地形提供商
var terrainProvider = this.terrainProvider;
// 水标记
var hasWaterMask =
this.showWaterEffect && // 水效果
terrainProvider.ready && // 地形准备好了
terrainProvider.hasWaterMask; // 水掩码
// 有水、有海洋法线
if (hasWaterMask && this._oceanNormalMapResourceDirty) {
// url changed, load new normal map asynchronously
// 海洋法线资源脏了
this._oceanNormalMapResourceDirty = false;
// 海洋法线资源url
var oceanNormalMapResource = this._oceanNormalMapResource;
var oceanNormalMapUrl = oceanNormalMapResource.url;
// url存在
if (defined(oceanNormalMapUrl)) {
var that = this;
// 获取海洋法线图片
when(oceanNormalMapResource.fetchImage(), function (image) {
if (oceanNormalMapUrl !== that._oceanNormalMapResource.url) {
// url changed while we were loading
// 图片请求过程中,url改变又改变了,需要重新请求
return;
}
// 销毁原来的法线图
that._oceanNormalMap =
that._oceanNormalMap && that._oceanNormalMap.destroy();
// 重新创建法线纹理
that._oceanNormalMap = new Texture({
context: frameState.context,
source: image,
});
});
} else {
// 法线没需求 销毁海洋法线
this._oceanNormalMap =
this._oceanNormalMap && this._oceanNormalMap.destroy();
}
}
// 过程(渲染、拾取等过程)
var pass = frameState.passes;
// 模式2D~3D
var mode = frameState.mode;
// 渲染过程
if (pass.render) {
// 显示大气
if (this.showGroundAtmosphere) {
// 减小大气高光强度
this._zoomedOutOceanSpecularIntensity = 0.4;
} else {
// 减小大气高光强度
this._zoomedOutOceanSpecularIntensity = 0.5;
}
// 更新QuadtreePrimitive数据 最大误差、瓦片缓存、降序加载、预加载祖先、预加载兄弟
surface.maximumScreenSpaceError = this.maximumScreenSpaceError;
surface.tileCacheSize = this.tileCacheSize;
surface.loadingDescendantLimit = this.loadingDescendantLimit;
surface.preloadAncestors = this.preloadAncestors;
surface.preloadSiblings = this.preloadSiblings;
// 更新GlobeSurfaceTileProvider数据 地形提供商、灯光隐藏、显示等很多的效果
tileProvider.terrainProvider = this.terrainProvider;
tileProvider.lightingFadeOutDistance = this.lightingFadeOutDistance;
tileProvider.lightingFadeInDistance = this.lightingFadeInDistance;
tileProvider.nightFadeOutDistance = this.nightFadeOutDistance;
tileProvider.nightFadeInDistance = this.nightFadeInDistance;
tileProvider.zoomedOutOceanSpecularIntensity =
mode === SceneMode.SCENE3D ? this._zoomedOutOceanSpecularIntensity : 0.0; // 3D环境下
tileProvider.hasWaterMask = hasWaterMask;
tileProvider.oceanNormalMap = this._oceanNormalMap;
tileProvider.enableLighting = this.enableLighting;
tileProvider.dynamicAtmosphereLighting = this.dynamicAtmosphereLighting;
tileProvider.dynamicAtmosphereLightingFromSun = this.dynamicAtmosphereLightingFromSun;
tileProvider.showGroundAtmosphere = this.showGroundAtmosphere;
tileProvider.shadows = this.shadows;
tileProvider.hueShift = this.atmosphereHueShift;
tileProvider.saturationShift = this.atmosphereSaturationShift;
tileProvider.brightnessShift = this.atmosphereBrightnessShift;
tileProvider.fillHighlightColor = this.fillHighlightColor;
tileProvider.showSkirts = this.showSkirts;
tileProvider.backFaceCulling = this.backFaceCulling;
tileProvider.undergroundColor = this._undergroundColor;
tileProvider.undergroundColorAlphaByDistance = this._undergroundColorAlphaByDistance;
// 更新
surface.beginFrame(frameState);
}
};
render:更新材质、创建渲染命令、将渲染命令添加到渲染队列中;
Globe.prototype.render = function (frameState) {
// 不显示就返回
if (!this.show) {
return;
}
if (defined(this._material)) {
// 更新材质
this._material.update(frameState.context);
}
// 更新地球表面
this._surface.render(frameState);
};
endFrame: 更新地球面上的瓦片数据(包括瓦片的检索、地形下载、影像下载、地形与影像的配合)、gpu资源的创建
Globe.prototype.endFrame = function (frameState) {
// 不显示就返回
if (!this.show) {
return;
}
// 是显示状态
if (frameState.passes.render) {
this._surface.endFrame(frameState);
}
};