瓦片创建DrawCommand的函数
addDrawCommandsForTile
- 1.
检查是否已为该瓦片创建了填充几何体。如果没有,则创建一个简单的填充几何体。
- 2.
添加地形数据的版权信息。
- 3.
根据不同的渲染模式和地形设置,确定需要的渲染参数和纹理单元数量。
- 4.
准备一些着色器所需的属性,如地形的高度范围、颜色校正参数等。
- 5.
遍历该瓦片的所有图像(imagery),设置对应的纹理和纹理参数。
- 6.
创建并配置绘制命令(DrawCommand),设置绘制所需的着色器程序、纹理、uniform 变量等。
- 7.
添加绘制命令到渲染队列,并设置绘制顺序和渲染状态。
- 8.
在需要时更新地球透明度状态。
js
/**
* 创建drawCommands,给uniform赋值
* @param {*} tileProvider
* @param {*} tile
* @param {*} frameState
*/
/*
1.检查是否已为该瓦片创建了填充几何体。如果没有,则创建一个简单的填充几何体。
2.添加地形数据的版权信息。
3.根据不同的渲染模式和地形设置,确定需要的渲染参数和纹理单元数量。
4.准备一些着色器所需的属性,如地形的高度范围、颜色校正参数等。
5.遍历该瓦片的所有图像(imagery),设置对应的纹理和纹理参数。
6.创建并配置绘制命令(DrawCommand),设置绘制所需的着色器程序、纹理、uniform 变量等。
7.添加绘制命令到渲染队列,并设置绘制顺序和渲染状态。
8.在需要时更新地球透明度状态。*/
function addDrawCommandsForTile(tileProvider, tile, frameState) {
const surfaceTile = tile.data;
if (!defined(surfaceTile.vertexArray)) {
if (surfaceTile.fill === undefined) {
//没有为此瓦片创建填充,可能是因为此瓦片未连接到任何可渲染的瓦片。
//因此,在瓷砖的可能高度范围中间创建一个简单的瓦片。
surfaceTile.fill = new TerrainFillMesh(tile);
}
surfaceTile.fill.update(tileProvider, frameState);
}
const creditDisplay = frameState.creditDisplay;
const terrainData = surfaceTile.terrainData;
if (defined(terrainData) && defined(terrainData.credits)) {
const tileCredits = terrainData.credits;
for (
let tileCreditIndex = 0, tileCreditLength = tileCredits.length;
tileCreditIndex < tileCreditLength;
++tileCreditIndex
) {
creditDisplay.addCredit(tileCredits[tileCreditIndex]);
}
}
// 获取图形上下文中可用的最大纹理图像单元
let maxTextures = ContextLimits.maximumTextureImageUnits;
// 水面遮罩纹理和平移/缩放信息
let waterMaskTexture = surfaceTile.waterMaskTexture;
let waterMaskTranslationAndScale = surfaceTile.waterMaskTranslationAndScale;
if (!defined(waterMaskTexture) && defined(surfaceTile.fill)) {
// 如果水面遮罩纹理未定义,并且表面瓦片具有fill属性,则使用fill的水面遮罩纹理和平移/缩放信息
waterMaskTexture = surfaceTile.fill.waterMaskTexture;
waterMaskTranslationAndScale =
surfaceTile.fill.waterMaskTranslationAndScale;
}
// 相机是否在地下
const cameraUnderground = frameState.cameraUnderground;
// 地球半透明状态
const globeTranslucencyState = frameState.globeTranslucencyState;
const translucent = globeTranslucencyState.translucent;
const frontFaceAlphaByDistance =
globeTranslucencyState.frontFaceAlphaByDistance;
const backFaceAlphaByDistance =
globeTranslucencyState.backFaceAlphaByDistance;
const translucencyRectangle = globeTranslucencyState.rectangle;
// 地下颜色和地下颜色透明度根据距离的默认值
const undergroundColor = defaultValue(
tileProvider.undergroundColor,
defaultUndergroundColor
);
const undergroundColorAlphaByDistance = defaultValue(
tileProvider.undergroundColorAlphaByDistance,
defaultUndergroundColorAlphaByDistance
);
// 是否显示地下颜色
const showUndergroundColor =
isUndergroundVisible(tileProvider, frameState) &&
frameState.mode === SceneMode.SCENE3D &&
undergroundColor.alpha > 0.0 &&
(undergroundColorAlphaByDistance.nearValue > 0.0 ||
undergroundColorAlphaByDistance.farValue > 0.0);
// Lambert漫反射乘数
const lambertDiffuseMultiplier = tileProvider.lambertDiffuseMultiplier;
// 是否显示反射的海洋
const showReflectiveOcean =
tileProvider.hasWaterMask && defined(waterMaskTexture);
const oceanNormalMap = tileProvider.oceanNormalMap;
const showOceanWaves = showReflectiveOcean && defined(oceanNormalMap);
// 是否具有顶点法线
const hasVertexNormals =
tileProvider.terrainProvider.ready &&
tileProvider.terrainProvider.hasVertexNormals;
// 是否启用雾效
const enableFog =
frameState.fog.enabled && frameState.fog.renderable && !cameraUnderground;
// 是否显示地面大气层
const showGroundAtmosphere =
tileProvider.showGroundAtmosphere && frameState.mode === SceneMode.SCENE3D;
// 是否投射阴影
const castShadows =
ShadowMode.castShadows(tileProvider.shadows) && !translucent;
// 是否接收阴影
const receiveShadows =
ShadowMode.receiveShadows(tileProvider.shadows) && !translucent;
// 获取tileProvider对象的色调偏移(hueShift)、饱和度偏移(saturationShift)和亮度偏移(brightnessShift)属性
const hueShift = tileProvider.hueShift;
const saturationShift = tileProvider.saturationShift;
const brightnessShift = tileProvider.brightnessShift;
// 检查色调、饱和度和亮度是否接近于0
// 这里使用CesiumMath.equalsEpsilon函数来进行接近于0的比较,
// CesiumMath.EPSILON7是一个非常小的数值,用于指定比较的精度
let colorCorrect = !(
CesiumMath.equalsEpsilon(hueShift, 0.0, CesiumMath.EPSILON7) &&
CesiumMath.equalsEpsilon(saturationShift, 0.0, CesiumMath.EPSILON7) &&
CesiumMath.equalsEpsilon(brightnessShift, 0.0, CesiumMath.EPSILON7)
);
// 初始值设为false,只有当showGroundAtmosphere为true时才会进行后续计算
let perFragmentGroundAtmosphere = false;
if (showGroundAtmosphere) {
// 获取相机位置距离场景中心的距离
const cameraDistance = Cartesian3.magnitude(frameState.camera.positionWC);
// 获取tileProvider对象的nightFadeOutDistance属性,表示地球表面大气渐隐距离
const fadeOutDistance = tileProvider.nightFadeOutDistance;
// 判断相机距离是否大于地球表面大气渐隐距离,如果是,则启用perFragmentGroundAtmosphere
perFragmentGroundAtmosphere = cameraDistance > fadeOutDistance;
}
if (showReflectiveOcean) {
--maxTextures;
}
if (showOceanWaves) {
--maxTextures;
}
if (
defined(frameState.shadowState) &&
frameState.shadowState.shadowsEnabled
) {
--maxTextures;
}
if (
defined(tileProvider.clippingPlanes) &&
tileProvider.clippingPlanes.enabled
) {
--maxTextures;
}
maxTextures -= globeTranslucencyState.numberOfTextureUniforms;
const mesh = surfaceTile.renderedMesh;
let rtc = mesh.center;
//encoding: 这是一个表示渲染网格编码的变量。渲染网格的编码可以包含一些信息,比如颜色编码、法线编码等,用于在渲染过程中对网格进行解码,得到正确的颜色、法线等属性。
const encoding = mesh.encoding;
//这是一个表示地球表面瓦片包围区域的变量。地球表面的每个瓦片都有一个边界区域,它定义了该瓦片在地球表面的空间范围。这个区域在渲染过程中可能会用于裁剪和优化绘制
const tileBoundingRegion = surfaceTile.tileBoundingRegion;
const exaggeration = frameState.terrainExaggeration;
const exaggerationRelativeHeight =
frameState.terrainExaggerationRelativeHeight;
const hasExaggeration = exaggeration !== 1.0;
//是否有表面法线
const hasGeodeticSurfaceNormals = encoding.hasGeodeticSurfaceNormals;
//在3D中不用
const tileRectangle = tileRectangleScratch;
// 仅仅在墨卡托投影中使用
let southLatitude = 0.0;
let northLatitude = 0.0;
let southMercatorY = 0.0;
let oneOverMercatorHeight = 0.0;
let useWebMercatorProjection = false;
if (frameState.mode !== SceneMode.SCENE3D) {
const projection = frameState.mapProjection;
const southwest = projection.project(
Rectangle.southwest(tile.rectangle),
southwestScratch
);
const northeast = projection.project(
Rectangle.northeast(tile.rectangle),
northeastScratch
);
tileRectangle.x = southwest.x;
tileRectangle.y = southwest.y;
tileRectangle.z = northeast.x;
tileRectangle.w = northeast.y;
// In 2D and Columbus View, use the center of the tile for RTC rendering.
if (frameState.mode !== SceneMode.MORPHING) {
rtc = rtcScratch;
rtc.x = 0.0;
rtc.y = (tileRectangle.z + tileRectangle.x) * 0.5;
rtc.z = (tileRectangle.w + tileRectangle.y) * 0.5;
tileRectangle.x -= rtc.y;
tileRectangle.y -= rtc.z;
tileRectangle.z -= rtc.y;
tileRectangle.w -= rtc.z;
}
if (
frameState.mode === SceneMode.SCENE2D &&
encoding.quantization === TerrainQuantization.BITS12
) {
// In 2D, the texture coordinates of the tile are interpolated over the rectangle to get the position in the vertex shader.
// When the texture coordinates are quantized, error is introduced. This can be seen through the 1px wide cracking
// between the quantized tiles in 2D. To compensate for the error, move the expand the rectangle in each direction by
// half the error amount.
const epsilon = (1.0 / (Math.pow(2.0, 12.0) - 1.0)) * 0.5;
const widthEpsilon = (tileRectangle.z - tileRectangle.x) * epsilon;
const heightEpsilon = (tileRectangle.w - tileRectangle.y) * epsilon;
tileRectangle.x -= widthEpsilon;
tileRectangle.y -= heightEpsilon;
tileRectangle.z += widthEpsilon;
tileRectangle.w += heightEpsilon;
}
if (projection instanceof WebMercatorProjection) {
southLatitude = tile.rectangle.south;
northLatitude = tile.rectangle.north;
southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(
southLatitude
);
oneOverMercatorHeight =
1.0 /
(WebMercatorProjection.geodeticLatitudeToMercatorAngle(northLatitude) -
southMercatorY);
useWebMercatorProjection = true;
}
}
// 使用预先定义的 surfaceShaderSetOptionsScratch 对象来存储地表着色器的选项
const surfaceShaderSetOptions = surfaceShaderSetOptionsScratch;
surfaceShaderSetOptions.frameState = frameState;
surfaceShaderSetOptions.surfaceTile = surfaceTile;
surfaceShaderSetOptions.showReflectiveOcean = showReflectiveOcean;
surfaceShaderSetOptions.showOceanWaves = showOceanWaves;
surfaceShaderSetOptions.enableLighting = tileProvider.enableLighting;
surfaceShaderSetOptions.dynamicAtmosphereLighting =
tileProvider.dynamicAtmosphereLighting;
surfaceShaderSetOptions.dynamicAtmosphereLightingFromSun =
tileProvider.dynamicAtmosphereLightingFromSun;
surfaceShaderSetOptions.showGroundAtmosphere = showGroundAtmosphere;
surfaceShaderSetOptions.atmosphereLightIntensity =
tileProvider.atmosphereLightIntensity;
surfaceShaderSetOptions.atmosphereRayleighCoefficient =
tileProvider.atmosphereRayleighCoefficient;
surfaceShaderSetOptions.atmosphereMieCoefficient =
tileProvider.atmosphereMieCoefficient;
surfaceShaderSetOptions.atmosphereRayleighScaleHeight =
tileProvider.atmosphereRayleighScaleHeight;
surfaceShaderSetOptions.atmosphereMieScaleHeight =
tileProvider.atmosphereMieScaleHeight;
surfaceShaderSetOptions.atmosphereMieAnisotropy =
tileProvider.atmosphereMieAnisotropy;
surfaceShaderSetOptions.perFragmentGroundAtmosphere = perFragmentGroundAtmosphere;
surfaceShaderSetOptions.hasVertexNormals = hasVertexNormals;
surfaceShaderSetOptions.useWebMercatorProjection = useWebMercatorProjection;
surfaceShaderSetOptions.clippedByBoundaries = surfaceTile.clippedByBoundaries;
surfaceShaderSetOptions.hasGeodeticSurfaceNormals = hasGeodeticSurfaceNormals;
surfaceShaderSetOptions.hasExaggeration = hasExaggeration;
const tileImageryCollection = surfaceTile.imagery;
let imageryIndex = 0;
const imageryLen = tileImageryCollection.length;
//howSkirts: 一个布尔值,表示是否显示地表瓦片的边缘(skirts)。skirts 是地表瓦片的边缘带,用于补充瓦片之间的高度差异,使得地表渲染过渡更加平滑。
const showSkirts =
tileProvider.showSkirts && !cameraUnderground && !translucent;
//backFaceCulling: 一个布尔值,表示是否开启背面剔除。背面剔除是图形渲染中的一种优化技术,它可以排除掉不可见的背面三角形,提高渲染性能。
const backFaceCulling =
tileProvider.backFaceCulling && !cameraUnderground && !translucent;
//firstPassRenderState: 一个渲染状态对象,表示第一遍渲染时的渲染状态。如果启用了背面剔除,将使用 tileProvider._renderState 的渲染状态;否则,将使用 tileProvider._disableCullingRenderState 的渲染状态。
const firstPassRenderState = backFaceCulling
? tileProvider._renderState
: tileProvider._disableCullingRenderState;
//一个渲染状态对象,表示其他遍渲染时的渲染状态。
const otherPassesRenderState = backFaceCulling
? tileProvider._blendRenderState
: tileProvider._disableCullingBlendRenderState;
let renderState = firstPassRenderState;
let initialColor = tileProvider._firstPassInitialColor;
const context = frameState.context;
if (!defined(tileProvider._debug.boundingSphereTile)) {
debugDestroyPrimitive();
}
const materialUniformMapChanged =
tileProvider._materialUniformMap !== tileProvider.materialUniformMap;
if (materialUniformMapChanged) {
//将当前的 materialUniformMap 赋值给 _materialUniformMap,以便在下一次比较时进行更新。
//然后,它遍历 tileProvider._drawCommands 数组,并为每个 draw command 创建一个新的 uniform map,然后将其存储在 tileProvider._uniformMaps 数组中。
tileProvider._materialUniformMap = tileProvider.materialUniformMap;
const drawCommandsLength = tileProvider._drawCommands.length;
for (let i = 0; i < drawCommandsLength; ++i) {
tileProvider._uniformMaps[i] = createTileUniformMap(
frameState,
tileProvider
);
}
}
do {
let numberOfDayTextures = 0;
let command;
let uniformMap;
if (tileProvider._drawCommands.length <= tileProvider._usedDrawCommands) {
command = new DrawCommand();
command.owner = tile;
command.cull = false;
command.boundingVolume = new BoundingSphere();
command.orientedBoundingBox = undefined;
uniformMap = createTileUniformMap(frameState, tileProvider);
tileProvider._drawCommands.push(command);
tileProvider._uniformMaps.push(uniformMap);
} else {
command = tileProvider._drawCommands[tileProvider._usedDrawCommands];
uniformMap = tileProvider._uniformMaps[tileProvider._usedDrawCommands];
}
command.owner = tile;
++tileProvider._usedDrawCommands;
if (tile === tileProvider._debug.boundingSphereTile) {
const obb = tileBoundingRegion.boundingVolume;
const boundingSphere = tileBoundingRegion.boundingSphere;
//如果此瓦片已经存在调试图元,则不会重新创建它,以避免分配每个帧。如果可以选择多个瓦片,则必须更改。
if (defined(obb)) {
getDebugOrientedBoundingBox(obb, Color.RED).update(frameState);
} else if (defined(boundingSphere)) {
getDebugBoundingSphere(boundingSphere, Color.RED).update(frameState);
}
}
const uniformMapProperties = uniformMap.properties;
Cartesian4.clone(initialColor, uniformMapProperties.initialColor);
uniformMapProperties.oceanNormalMap = oceanNormalMap;
uniformMapProperties.lightingFadeDistance.x =
tileProvider.lightingFadeOutDistance;
uniformMapProperties.lightingFadeDistance.y =
tileProvider.lightingFadeInDistance;
uniformMapProperties.nightFadeDistance.x =
tileProvider.nightFadeOutDistance;
uniformMapProperties.nightFadeDistance.y = tileProvider.nightFadeInDistance;
uniformMapProperties.atmosphereLightIntensity =
tileProvider.atmosphereLightIntensity;
uniformMapProperties.atmosphereRayleighCoefficient =
tileProvider.atmosphereRayleighCoefficient;
uniformMapProperties.atmosphereMieCoefficient =
tileProvider.atmosphereMieCoefficient;
uniformMapProperties.atmosphereRayleighScaleHeight =
tileProvider.atmosphereRayleighScaleHeight;
uniformMapProperties.atmosphereMieScaleHeight =
tileProvider.atmosphereMieScaleHeight;
uniformMapProperties.atmosphereMieAnisotropy =
tileProvider.atmosphereMieAnisotropy;
uniformMapProperties.zoomedOutOceanSpecularIntensity =
tileProvider.zoomedOutOceanSpecularIntensity;
const frontFaceAlphaByDistanceFinal = cameraUnderground
? backFaceAlphaByDistance
: frontFaceAlphaByDistance;
const backFaceAlphaByDistanceFinal = cameraUnderground
? frontFaceAlphaByDistance
: backFaceAlphaByDistance;
if (defined(frontFaceAlphaByDistanceFinal)) {
Cartesian4.fromElements(
frontFaceAlphaByDistanceFinal.near,
frontFaceAlphaByDistanceFinal.nearValue,
frontFaceAlphaByDistanceFinal.far,
frontFaceAlphaByDistanceFinal.farValue,
uniformMapProperties.frontFaceAlphaByDistance
);
Cartesian4.fromElements(
backFaceAlphaByDistanceFinal.near,
backFaceAlphaByDistanceFinal.nearValue,
backFaceAlphaByDistanceFinal.far,
backFaceAlphaByDistanceFinal.farValue,
uniformMapProperties.backFaceAlphaByDistance
);
}
Cartesian4.fromElements(
undergroundColorAlphaByDistance.near,
undergroundColorAlphaByDistance.nearValue,
undergroundColorAlphaByDistance.far,
undergroundColorAlphaByDistance.farValue,
uniformMapProperties.undergroundColorAlphaByDistance
);
Color.clone(undergroundColor, uniformMapProperties.undergroundColor);
uniformMapProperties.lambertDiffuseMultiplier = lambertDiffuseMultiplier;
const highlightFillTile =
!defined(surfaceTile.vertexArray) &&
defined(tileProvider.fillHighlightColor) &&
tileProvider.fillHighlightColor.alpha > 0.0;
if (highlightFillTile) {
Color.clone(
tileProvider.fillHighlightColor,
uniformMapProperties.fillHighlightColor
);
}
uniformMapProperties.terrainExaggerationAndRelativeHeight.x = exaggeration;
uniformMapProperties.terrainExaggerationAndRelativeHeight.y = exaggerationRelativeHeight;
uniformMapProperties.center3D = mesh.center;
Cartesian3.clone(rtc, uniformMapProperties.rtc);
Cartesian4.clone(tileRectangle, uniformMapProperties.tileRectangle);
uniformMapProperties.southAndNorthLatitude.x = southLatitude;
uniformMapProperties.southAndNorthLatitude.y = northLatitude;
uniformMapProperties.southMercatorYAndOneOverHeight.x = southMercatorY;
uniformMapProperties.southMercatorYAndOneOverHeight.y = oneOverMercatorHeight;
// Convert tile limiter rectangle from cartographic to texture space using the tileRectangle.
const localizedCartographicLimitRectangle = localizedCartographicLimitRectangleScratch;
const cartographicLimitRectangle = clipRectangleAntimeridian(
tile.rectangle,
tileProvider.cartographicLimitRectangle
);
const localizedTranslucencyRectangle = localizedTranslucencyRectangleScratch;
const clippedTranslucencyRectangle = clipRectangleAntimeridian(
tile.rectangle,
translucencyRectangle
);
Cartesian3.fromElements(
hueShift,
saturationShift,
brightnessShift,
uniformMapProperties.hsbShift
);
const cartographicTileRectangle = tile.rectangle;
const inverseTileWidth = 1.0 / cartographicTileRectangle.width;
const inverseTileHeight = 1.0 / cartographicTileRectangle.height;
localizedCartographicLimitRectangle.x =
(cartographicLimitRectangle.west - cartographicTileRectangle.west) *
inverseTileWidth;
localizedCartographicLimitRectangle.y =
(cartographicLimitRectangle.south - cartographicTileRectangle.south) *
inverseTileHeight;
localizedCartographicLimitRectangle.z =
(cartographicLimitRectangle.east - cartographicTileRectangle.west) *
inverseTileWidth;
localizedCartographicLimitRectangle.w =
(cartographicLimitRectangle.north - cartographicTileRectangle.south) *
inverseTileHeight;
Cartesian4.clone(
localizedCartographicLimitRectangle,
uniformMapProperties.localizedCartographicLimitRectangle
);
localizedTranslucencyRectangle.x =
(clippedTranslucencyRectangle.west - cartographicTileRectangle.west) *
inverseTileWidth;
localizedTranslucencyRectangle.y =
(clippedTranslucencyRectangle.south - cartographicTileRectangle.south) *
inverseTileHeight;
localizedTranslucencyRectangle.z =
(clippedTranslucencyRectangle.east - cartographicTileRectangle.west) *
inverseTileWidth;
localizedTranslucencyRectangle.w =
(clippedTranslucencyRectangle.north - cartographicTileRectangle.south) *
inverseTileHeight;
Cartesian4.clone(
localizedTranslucencyRectangle,
uniformMapProperties.localizedTranslucencyRectangle
);
// 为了提高性能,仅当瓦片处于雾中时才在着色器中使用雾。
const applyFog =
enableFog &&
CesiumMath.fog(tile._distance, frameState.fog.density) >
CesiumMath.EPSILON3;
colorCorrect = colorCorrect && (applyFog || showGroundAtmosphere);
let applyBrightness = false;
let applyContrast = false;
let applyHue = false;
let applySaturation = false;
let applyGamma = false;
let applyAlpha = false;
let applyDayNightAlpha = false;
let applySplit = false;
let applyCutout = false;
let applyColorToAlpha = false;
while (numberOfDayTextures < maxTextures && imageryIndex < imageryLen) {
const tileImagery = tileImageryCollection[imageryIndex];
const imagery = tileImagery.readyImagery;
++imageryIndex;
if (!defined(imagery) || imagery.imageryLayer.alpha === 0.0) {
continue;
}
const texture = tileImagery.useWebMercatorT
? imagery.textureWebMercator
: imagery.texture;
//>>includeStart('debug', pragmas.debug);
if (!defined(texture)) {
// Our "ready" texture isn't actually ready. This should never happen.
//
// Side note: It IS possible for it to not be in the READY ImageryState, though.
// This can happen when a single imagery tile is shared by two terrain tiles (common)
// and one of them (A) needs a geographic version of the tile because it is near the poles,
// and the other (B) does not. B can and will transition the imagery tile to the READY state
// without reprojecting to geographic. Then, later, A will deem that same tile not-ready-yet
// because it only has the Web Mercator texture, and flip it back to the TRANSITIONING state.
// The imagery tile won't be in the READY state anymore, but it's still READY enough for B's
// purposes.
throw new DeveloperError("readyImagery is not actually ready!");
}
//>>includeEnd('debug');
const imageryLayer = imagery.imageryLayer;
if (!defined(tileImagery.textureTranslationAndScale)) {
tileImagery.textureTranslationAndScale = imageryLayer._calculateTextureTranslationAndScale(
tile,
tileImagery
);
}
uniformMapProperties.dayTextures[numberOfDayTextures] = texture;
uniformMapProperties.dayTextureTranslationAndScale[numberOfDayTextures] =
tileImagery.textureTranslationAndScale;
uniformMapProperties.dayTextureTexCoordsRectangle[numberOfDayTextures] =
tileImagery.textureCoordinateRectangle;
uniformMapProperties.dayTextureUseWebMercatorT[numberOfDayTextures] =
tileImagery.useWebMercatorT;
uniformMapProperties.dayTextureAlpha[numberOfDayTextures] =
imageryLayer.alpha;
applyAlpha =
applyAlpha ||
uniformMapProperties.dayTextureAlpha[numberOfDayTextures] !== 1.0;
uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] =
imageryLayer.nightAlpha;
applyDayNightAlpha =
applyDayNightAlpha ||
uniformMapProperties.dayTextureNightAlpha[numberOfDayTextures] !== 1.0;
uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] =
imageryLayer.dayAlpha;
applyDayNightAlpha =
applyDayNightAlpha ||
uniformMapProperties.dayTextureDayAlpha[numberOfDayTextures] !== 1.0;
uniformMapProperties.dayTextureBrightness[numberOfDayTextures] =
imageryLayer.brightness;
applyBrightness =
applyBrightness ||
uniformMapProperties.dayTextureBrightness[numberOfDayTextures] !==
ImageryLayer.DEFAULT_BRIGHTNESS;
uniformMapProperties.dayTextureContrast[numberOfDayTextures] =
imageryLayer.contrast;
applyContrast =
applyContrast ||
uniformMapProperties.dayTextureContrast[numberOfDayTextures] !==
ImageryLayer.DEFAULT_CONTRAST;
uniformMapProperties.dayTextureHue[numberOfDayTextures] =
imageryLayer.hue;
applyHue =
applyHue ||
uniformMapProperties.dayTextureHue[numberOfDayTextures] !==
ImageryLayer.DEFAULT_HUE;
uniformMapProperties.dayTextureSaturation[numberOfDayTextures] =
imageryLayer.saturation;
applySaturation =
applySaturation ||
uniformMapProperties.dayTextureSaturation[numberOfDayTextures] !==
ImageryLayer.DEFAULT_SATURATION;
uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] =
1.0 / imageryLayer.gamma;
applyGamma =
applyGamma ||
uniformMapProperties.dayTextureOneOverGamma[numberOfDayTextures] !==
1.0 / ImageryLayer.DEFAULT_GAMMA;
uniformMapProperties.dayTextureSplit[numberOfDayTextures] =
imageryLayer.splitDirection;
applySplit =
applySplit ||
uniformMapProperties.dayTextureSplit[numberOfDayTextures] !== 0.0;
//更新剪切矩形
let dayTextureCutoutRectangle =
uniformMapProperties.dayTextureCutoutRectangles[numberOfDayTextures];
if (!defined(dayTextureCutoutRectangle)) {
dayTextureCutoutRectangle = uniformMapProperties.dayTextureCutoutRectangles[
numberOfDayTextures
] = new Cartesian4();
}
Cartesian4.clone(Cartesian4.ZERO, dayTextureCutoutRectangle);
if (defined(imageryLayer.cutoutRectangle)) {
const cutoutRectangle = clipRectangleAntimeridian(
cartographicTileRectangle,
imageryLayer.cutoutRectangle
);
const intersection = Rectangle.simpleIntersection(
cutoutRectangle,
cartographicTileRectangle,
rectangleIntersectionScratch
);
applyCutout = defined(intersection) || applyCutout;
dayTextureCutoutRectangle.x =
(cutoutRectangle.west - cartographicTileRectangle.west) *
inverseTileWidth;
dayTextureCutoutRectangle.y =
(cutoutRectangle.south - cartographicTileRectangle.south) *
inverseTileHeight;
dayTextureCutoutRectangle.z =
(cutoutRectangle.east - cartographicTileRectangle.west) *
inverseTileWidth;
dayTextureCutoutRectangle.w =
(cutoutRectangle.north - cartographicTileRectangle.south) *
inverseTileHeight;
}
// 将颜色更新为alpha
let colorToAlpha =
uniformMapProperties.colorsToAlpha[numberOfDayTextures];
if (!defined(colorToAlpha)) {
colorToAlpha = uniformMapProperties.colorsToAlpha[
numberOfDayTextures
] = new Cartesian4();
}
const hasColorToAlpha =
defined(imageryLayer.colorToAlpha) &&
imageryLayer.colorToAlphaThreshold > 0.0;
applyColorToAlpha = applyColorToAlpha || hasColorToAlpha;
if (hasColorToAlpha) {
const color = imageryLayer.colorToAlpha;
colorToAlpha.x = color.red;
colorToAlpha.y = color.green;
colorToAlpha.z = color.blue;
colorToAlpha.w = imageryLayer.colorToAlphaThreshold;
} else {
colorToAlpha.w = -1.0;
}
if (defined(imagery.credits)) {
const credits = imagery.credits;
for (
let creditIndex = 0, creditLength = credits.length;
creditIndex < creditLength;
++creditIndex
) {
creditDisplay.addCredit(credits[creditIndex]);
}
}
++numberOfDayTextures;
}
//将纹理数组修剪到使用过的长度,这样我们就不会使用最终可能被破坏的旧纹理
uniformMapProperties.dayTextures.length = numberOfDayTextures;
uniformMapProperties.waterMask = waterMaskTexture;
Cartesian4.clone(
waterMaskTranslationAndScale,
uniformMapProperties.waterMaskTranslationAndScale
);
uniformMapProperties.minMaxHeight.x = encoding.minimumHeight;
uniformMapProperties.minMaxHeight.y = encoding.maximumHeight;
Matrix4.clone(encoding.matrix, uniformMapProperties.scaleAndBias);
// update clipping planes
const clippingPlanes = tileProvider._clippingPlanes;
const clippingPlanesEnabled =
defined(clippingPlanes) && clippingPlanes.enabled && tile.isClipped;
if (clippingPlanesEnabled) {
uniformMapProperties.clippingPlanesEdgeColor = Color.clone(
clippingPlanes.edgeColor,
uniformMapProperties.clippingPlanesEdgeColor
);
uniformMapProperties.clippingPlanesEdgeWidth = clippingPlanes.edgeWidth;
}
surfaceShaderSetOptions.numberOfDayTextures = numberOfDayTextures;
surfaceShaderSetOptions.applyBrightness = applyBrightness;
surfaceShaderSetOptions.applyContrast = applyContrast;
surfaceShaderSetOptions.applyHue = applyHue;
surfaceShaderSetOptions.applySaturation = applySaturation;
surfaceShaderSetOptions.applyGamma = applyGamma;
surfaceShaderSetOptions.applyAlpha = applyAlpha;
surfaceShaderSetOptions.applyDayNightAlpha = applyDayNightAlpha;
surfaceShaderSetOptions.applySplit = applySplit;
surfaceShaderSetOptions.enableFog = applyFog;
surfaceShaderSetOptions.enableClippingPlanes = clippingPlanesEnabled;
surfaceShaderSetOptions.clippingPlanes = clippingPlanes;
surfaceShaderSetOptions.hasImageryLayerCutout = applyCutout;
surfaceShaderSetOptions.colorCorrect = colorCorrect;
surfaceShaderSetOptions.highlightFillTile = highlightFillTile;
surfaceShaderSetOptions.colorToAlpha = applyColorToAlpha;
surfaceShaderSetOptions.showUndergroundColor = showUndergroundColor;
surfaceShaderSetOptions.translucent = translucent;
let count = surfaceTile.renderedMesh.indices.length;
if (!showSkirts) {
count = surfaceTile.renderedMesh.indexCountWithoutSkirts;
}
command.shaderProgram = tileProvider._surfaceShaderSet.getShaderProgram(
surfaceShaderSetOptions
);
command.castShadows = castShadows;
command.receiveShadows = receiveShadows;
command.renderState = renderState;
command.primitiveType = PrimitiveType.TRIANGLES;
command.vertexArray =
surfaceTile.vertexArray || surfaceTile.fill.vertexArray;
command.count = count;
command.uniformMap = uniformMap;
command.pass = Pass.GLOBE;
if (tileProvider._debug.wireframe) {
createWireframeVertexArrayIfNecessary(context, tileProvider, tile);
if (defined(surfaceTile.wireframeVertexArray)) {
command.vertexArray = surfaceTile.wireframeVertexArray;
command.primitiveType = PrimitiveType.LINES;
command.count = count * 2;
}
}
let boundingVolume = command.boundingVolume;
const orientedBoundingBox = command.orientedBoundingBox;
if (frameState.mode !== SceneMode.SCENE3D) {
BoundingSphere.fromRectangleWithHeights2D(
tile.rectangle,
frameState.mapProjection,
tileBoundingRegion.minimumHeight,
tileBoundingRegion.maximumHeight,
boundingVolume
);
Cartesian3.fromElements(
boundingVolume.center.z,
boundingVolume.center.x,
boundingVolume.center.y,
boundingVolume.center
);
if (frameState.mode === SceneMode.MORPHING) {
boundingVolume = BoundingSphere.union(
tileBoundingRegion.boundingSphere,
boundingVolume,
boundingVolume
);
}
} else {
command.boundingVolume = BoundingSphere.clone(
tileBoundingRegion.boundingSphere,
boundingVolume
);
command.orientedBoundingBox = OrientedBoundingBox.clone(
tileBoundingRegion.boundingVolume,
orientedBoundingBox
);
}
command.dirty = true;
if (translucent) {
globeTranslucencyState.updateDerivedCommands(command, frameState);
}
pushCommand(command, frameState);
renderState = otherPassesRenderState;
initialColor = otherPassesInitialColor;
} while (imageryIndex < imageryLen);
}
顶点着色器
c++
// 根据编译指令选择使用压缩顶点属性还是非压缩顶点属性
#ifdef QUANTIZATION_BITS12
// 使用压缩顶点属性,包含以下属性:
attribute vec4 compressed0;//压缩的二维位置信息 (x, y),以及压缩的高度信息 (z)
attribute float compressed1;//压缩的纹理坐标信息
#else
attribute vec4 position3DAndHeight;//三维位置信息 (x, y, z),以及高度信息 (w)
attribute vec4 textureCoordAndEncodedNormals;// 纹理坐标信息 (x, y),以及压缩的法线信息 (z)
#endif
//是否使用地表几何表面法线信息
#ifdef GEODETIC_SURFACE_NORMALS
// 使用地表几何表面法线信息,包含属性 geodeticSurfaceNormal
attribute vec3 geodeticSurfaceNormal;
#endif
//是否使用地形高度 exaggeration
#ifdef EXAGGERATION
// 地形高度 exaggeration 和 relativeHeight,用于对地形进行高度放大
uniform vec2 u_terrainExaggerationAndRelativeHeight;
#endif
uniform vec3 u_center3D; // 地形瓦片的中心位置 (x, y, z)
uniform mat4 u_modifiedModelView;// 修改后的模型视图矩阵,用于计算顶点在摄像机坐标系中的位置
uniform mat4 u_modifiedModelViewProjection;// 修改后的模型视图投影矩阵,用于计算顶点在裁剪空间中的位置
uniform vec4 u_tileRectangle;// 地形瓦片的矩形范围 (st 表示左下角,pq 表示右上角)
// 2D 墨卡托投影相关 uniform 变量的声明
uniform vec2 u_southAndNorthLatitude;// 南纬和北纬,用于计算墨卡托投影中的位置比例
uniform vec2 u_southMercatorYAndOneOverHeight;// 南纬墨卡托投影的 Y 坐标和高度的倒数
// 以下是 varying 变量的声明,用于将计算得到的变量传递给片段着色器
varying vec3 v_positionMC;// 顶点在模型坐标系中的位置 (Model Coordinates)
varying vec3 v_positionEC;// 顶点在摄像机坐标系中的位置 (Eye Coordinates)
varying vec3 v_textureCoordinates;// 顶点在纹理坐标系中的位置 (Texture Coordinates)
varying vec3 v_normalMC;// 顶点在模型坐标系中的法线 (Model Coordinates Normal)
varying vec3 v_normalEC;// 顶点在摄像机坐标系中的法线 (Eye Coordinates Normal)
// 材质应用相关 varying 变量的声明
#ifdef APPLY_MATERIAL
// 用于存储当前顶点的坡度 (slope) 的 varying 变量
// 坡度是指地形表面法线和垂直方向之间的夹角,用于在着色器中实现阴影效果等功能
varying float v_slope;
// 用于存储当前顶点的坡向 (aspect) 的 varying 变量
// 坡向是指地形表面法线和东西方向之间的夹角,用于在着色器中实现阴影效果等功能
varying float v_aspect;
// 用于存储当前顶点的高度 (height) 的 varying 变量
// 在着色器中用于实现基于高度的渲染效果,如山地和山谷的着色等
varying float v_height;
#endif
// 与雾、大气层、地下颜色、半透明效果相关的 varying 变量的声明
#if defined(FOG) || defined(GROUND_ATMOSPHERE) || defined(UNDERGROUND_COLOR) || defined(TRANSLUCENT)
// 用于存储当前顶点到相机位置的距离的 varying 变量
// 在一些特殊效果的计算中,距离相机的远近可能会影响颜色、透明度等渲染效果
varying float v_distance;
#endif
// 与雾和大气散射效果相关的 varying 变量的声明
#if defined(FOG) || defined(GROUND_ATMOSPHERE)
// 用于存储大气散射光线的 Rayleigh 颜色分量的 varying 变量
// Rayleigh 散射主要负责天空的散射效果,使天空呈现出蓝色,并且在地球表面可见部分产生不同的颜色渐变效果
varying vec3 v_atmosphereRayleighColor;
// 用于存储大气散射光线的 Mie 颜色分量的 varying 变量
// Mie 散射主要负责日落时的散射效果,使太阳的光线呈现出橙色和红色,并且在地球表面可见部分产生不同的颜色渐变效果
varying vec3 v_atmosphereMieColor;
// 用于存储大气散射效果的透明度的 varying 变量
// 大气散射会在视线中加入一些模糊和渐变效果,透明度决定了散射效果在场景中的强度和影响范围
varying float v_atmosphereOpacity;
#endif
// 这些函数是在运行时生成的。
// 这是一个函数声明,用于计算带有高度的位置,并根据纹理坐标返回四维向量
// 输入参数:
// position: 顶点的位置,通常是顶点的3D坐标
// height: 顶点的高度,用于在3D场景中提升或降低顶点的高度
// textureCoordinates: 纹理坐标,用于在着色器中进行纹理采样或其他计算
// 返回值:
// 一个四维向量,表示经过高度变换的顶点位置,通常包含变换后的x、y、z坐标以及w坐标
// w坐标通常设置为1.0,用于进行透视除法,以便将顶点从裁剪坐标系转换到屏幕坐标系
vec4 getPosition(vec3 position, float height, vec2 textureCoordinates);
// 这是一个函数声明,用于计算2D模式下的纹理坐标的y分数值
// 输入参数:
// textureCoordinates: 纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个浮点数,表示2D模式下纹理坐标的y分数值
float get2DYPositionFraction(vec2 textureCoordinates);
// 这是一个函数实现,用于在3D模式下计算顶点位置
// 输入参数:
// position: 顶点的位置,通常是顶点的3D坐标
// height: 顶点的高度,用于在3D场景中提升或降低顶点的高度
// textureCoordinates: 纹理坐标,用于在着色器中进行纹理采样或其他计算
// 返回值:
// 一个四维向量,表示3D模式下的经过变换后的顶点位置
// 这里直接使用了一个矩阵乘法运算,将顶点的位置变换到经过修改的模型视图投影矩阵中
// 通常用于处理3D场景中的顶点位置,如处理地球表面的顶点
vec4 getPosition3DMode(vec3 position, float height, vec2 textureCoordinates)
{
return u_modifiedModelViewProjection * vec4(position, 1.0);
}
// 计算2D墨卡托投影纹理坐标的y分数值
// 输入参数:
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个浮点数,表示2D墨卡托投影纹理坐标的y分数值
float get2DMercatorYPositionFraction(vec2 textureCoordinates)
{
// The width of a tile at level 11, in radians and assuming a single root tile, is
// 2.0 * czm_pi / pow(2.0, 11.0)
// We want to just linearly interpolate the 2D position from the texture coordinates
// when we're at this level or higher. The constant below is the expression
// above evaluated and then rounded up at the 4th significant digit.
// 在Cesium中,地球表面被分成多个级别的瓦片,每个瓦片对应不同的分辨率。
// 这里通过一个常量maxTileWidth来表示在第11级瓦片上的一个瓦片的宽度(单位:弧度)。
// 这个值通过计算得到,并将其四舍五入到第4位有效数字。
const float maxTileWidth = 0.003068;
// 获取顶点的纹理坐标的y分数值,即y轴上的位置
float positionFraction = textureCoordinates.y;
// 获取南北纬度范围
float southLatitude = u_southAndNorthLatitude.x;
float northLatitude = u_southAndNorthLatitude.y;
// 判断当前级别的瓦片是否超过了maxTileWidth,即瓦片的宽度是否足够小
// 如果超过了,说明处于较高的级别,此时需要进行2D墨卡托投影的处理
if (northLatitude - southLatitude > maxTileWidth)
{
// 获取南方墨卡托投影y坐标和高度的倒数
float southMercatorY = u_southMercatorYAndOneOverHeight.x;
float oneOverMercatorHeight = u_southMercatorYAndOneOverHeight.y;
// 根据纹理坐标的y值和南北纬度范围,计算当前纹理坐标对应的地理纬度
float currentLatitude = mix(southLatitude, northLatitude, textureCoordinates.y);
// 对地理纬度进行限制,确保其在墨卡托投影范围内
currentLatitude = clamp(currentLatitude, -czm_webMercatorMaxLatitude, czm_webMercatorMaxLatitude);
// 根据计算得到的地理纬度,以及南方墨卡托投影y坐标和高度的倒数,计算当前纹理坐标在墨卡托投影中的y分数值
positionFraction = czm_latitudeToWebMercatorFraction(currentLatitude, southMercatorY, oneOverMercatorHeight);
}
return positionFraction;
}
// 计算2D地理坐标纹理的y分数值
// 输入参数:
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个浮点数,表示2D地理坐标纹理的y分数值
float get2DGeographicYPositionFraction(vec2 textureCoordinates)
{
// 直接返回顶点的纹理坐标的y值作为2D地理坐标纹理的y分数值
return textureCoordinates.y;
}
// 计算平面地球上顶点位置
// 输入参数:
// position: 顶点的3D位置坐标,模型坐标系下
// height: 顶点的高度,通常是相对于地球椭球体表面的高度
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个4维向量,表示顶点的投影位置
vec4 getPositionPlanarEarth(vec3 position, float height, vec2 textureCoordinates)
{
// 使用get2DYPositionFraction函数计算2D地理坐标纹理的y分数值
float yPositionFraction = get2DYPositionFraction(textureCoordinates);
// 使用纹理坐标的x和计算得到的y分数值,从u_tileRectangle中混合计算出顶点在平面地球上的位置
vec4 rtcPosition2D = vec4(height, mix(u_tileRectangle.st, u_tileRectangle.pq, vec2(textureCoordinates.x, yPositionFraction)), 1.0);
// 返回顶点在经过变换后的模型视图投影坐标系下的位置
return u_modifiedModelViewProjection * rtcPosition2D;
}
// 计算平面地球上2D模式下顶点位置
// 输入参数:
// position: 顶点的3D位置坐标,模型坐标系下
// height: 顶点的高度,通常在平面地球模式下高度为0,因为忽略地球的曲率,顶点都位于地球表面上
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个4维向量,表示顶点的投影位置
vec4 getPosition2DMode(vec3 position, float height, vec2 textureCoordinates)
{
// 调用getPositionPlanarEarth函数,将height设为0来计算顶点在平面地球上的位置
return getPositionPlanarEarth(position, 0.0, textureCoordinates);
}
// 计算哥伦布视图模式下顶点位置
// 输入参数:
// position: 顶点的3D位置坐标,模型坐标系下
// height: 顶点的高度,通常在哥伦布视图模式下会使用真实的高度值
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个4维向量,表示顶点的投影位置
vec4 getPositionColumbusViewMode(vec3 position, float height, vec2 textureCoordinates)
{
// 调用getPositionPlanarEarth函数,将height参数设为真实的高度值来计算顶点在哥伦布视图模式下的位置
return getPositionPlanarEarth(position, height, textureCoordinates);
}
// 计算变形模式(morphing mode)下顶点位置
// 输入参数:
// position: 顶点的3D位置坐标,模型坐标系下
// height: 顶点的高度,通常在变形模式下会使用真实的高度值
// textureCoordinates: 顶点的纹理坐标,通常是经过插值后的顶点纹理坐标
// 返回值:
// 一个4维向量,表示顶点的投影位置
vec4 getPositionMorphingMode(vec3 position, float height, vec2 textureCoordinates)
{
// 在变形模式下,我们不进行RTC(Render to Texture)操作,因此顶点位置可能存在抖动。
// 不过,这种抖动通常不会被注意到。
// 将顶点的3D位置坐标与u_center3D相加,得到在世界坐标系下的顶点位置
vec3 position3DWC = position + u_center3D;
// 调用get2DYPositionFraction函数,计算顶点的2D Mercator投影中的y坐标分数
float yPositionFraction = get2DYPositionFraction(textureCoordinates);
// 根据textureCoordinates和yPositionFraction,计算顶点在2D Mercator投影下的位置
vec4 position2DWC = vec4(height, mix(u_tileRectangle.st, u_tileRectangle.pq, vec2(textureCoordinates.x, yPositionFraction)), 1.0);
// 调用czm_columbusViewMorph函数,将顶点在2D Mercator投影下的位置和在世界坐标系下的位置进行插值,
// 根据czm_morphTime参数进行顶点位置的变形(morphing)
vec4 morphPosition = czm_columbusViewMorph(position2DWC, vec4(position3DWC, 1.0), czm_morphTime);
// 将变形后的顶点位置通过czm_modelViewProjection矩阵进行投影,得到最终的顶点投影位置
return czm_modelViewProjection * morphPosition;
}
#ifdef QUANTIZATION_BITS12
// #ifdef QUANTIZATION_BITS12 时可用的uniform和矩阵变量
// uniform变量:顶点高度的最小值和最大值
uniform vec2 u_minMaxHeight;
// uniform变量:用于将压缩的顶点位置解压缩的缩放和偏移矩阵
uniform mat4 u_scaleAndBias;
#endif
void main()
{
//压缩顶点属性方式
#ifdef QUANTIZATION_BITS12
// 解压缩二维位置 (x, y) 和高度信息 (z, w)
vec2 xy = czm_decompressTextureCoordinates(compressed0.x);
vec2 zh = czm_decompressTextureCoordinates(compressed0.y);
vec3 position = vec3(xy, zh.x);
float height = zh.y;
// 解压缩纹理坐标信息
vec2 textureCoordinates = czm_decompressTextureCoordinates(compressed0.z);
// 根据高度信息的最小值和最大值对高度进行线性缩放和偏移
height = height * (u_minMaxHeight.y - u_minMaxHeight.x) + u_minMaxHeight.x;
// 应用缩放和偏移矩阵对位置进行变换
position = (u_scaleAndBias * vec4(position, 1.0)).xyz;
// 根据预处理指令和宏的定义来决定 webMercatorT 和 encodedNormal 的值
// 如果定义了 ENABLE_VERTEX_LIGHTING 或 GENERATE_POSITION_AND_NORMAL 并且定义了 INCLUDE_WEB_MERCATOR_Y 宏
// 则使用 compressed0.w 中的数据来解压缩 webMercatorT,使用 compressed1 作为 encodedNormal
#if (defined(ENABLE_VERTEX_LIGHTING) || defined(GENERATE_POSITION_AND_NORMAL)) && defined(INCLUDE_WEB_MERCATOR_Y)
float webMercatorT = czm_decompressTextureCoordinates(compressed0.w).x;
float encodedNormal = compressed1;
// 如果定义了 INCLUDE_WEB_MERCATOR_Y 宏,则使用 compressed0.w 中的数据来解压缩 webMercatorT,encodedNormal 为 0.0
#elif defined(INCLUDE_WEB_MERCATOR_Y)
float webMercatorT = czm_decompressTextureCoordinates(compressed0.w).x;
float encodedNormal = 0.0;
// 如果定义了 ENABLE_VERTEX_LIGHTING 或 GENERATE_POSITION_AND_NORMAL 宏
// 则使用 textureCoordinates.y 作为 webMercatorT,使用 compressed0.w 作为 encodedNormal
#elif defined(ENABLE_VERTEX_LIGHTING) || defined(GENERATE_POSITION_AND_NORMAL)
float webMercatorT = textureCoordinates.y;
float encodedNormal = compressed0.w;
// 如果没有定义上述任何宏,则使用 textureCoordinates.y 作为 webMercatorT,encodedNormal 为 0.0
#else
float webMercatorT = textureCoordinates.y;
float encodedNormal = 0.0;
#endif
#else
// A single float per element
// 非压缩顶点属性方式
// 获取三维位置信息和高度信息
vec3 position = position3DAndHeight.xyz;
float height = position3DAndHeight.w;
// 获取纹理坐标信息
vec2 textureCoordinates = textureCoordAndEncodedNormals.xy;
// 根据编译指令和是否使用地形高度 exaggeration 对顶点位置进行调整
// 根据预处理指令和宏的定义来决定 webMercatorT 和 encodedNormal 的值
// 如果定义了 ENABLE_VERTEX_LIGHTING 或 GENERATE_POSITION_AND_NORMAL 或 APPLY_MATERIAL 宏
// 并且定义了 INCLUDE_WEB_MERCATOR_Y 宏,则使用 textureCoordAndEncodedNormals.z 作为 webMercatorT,
// 使用 textureCoordAndEncodedNormals.w 作为 encodedNormal
#if (defined(ENABLE_VERTEX_LIGHTING) || defined(GENERATE_POSITION_AND_NORMAL) || defined(APPLY_MATERIAL)) && defined(INCLUDE_WEB_MERCATOR_Y)
float webMercatorT = textureCoordAndEncodedNormals.z;
float encodedNormal = textureCoordAndEncodedNormals.w;
// 如果定义了 ENABLE_VERTEX_LIGHTING 或 GENERATE_POSITION_AND_NORMAL 或 APPLY_MATERIAL 宏
// 但没有定义 INCLUDE_WEB_MERCATOR_Y 宏,则使用 textureCoordinates.y 作为 webMercatorT,
// 使用 textureCoordAndEncodedNormals.z 作为 encodedNormal
#elif defined(ENABLE_VERTEX_LIGHTING) || defined(GENERATE_POSITION_AND_NORMAL) || defined(APPLY_MATERIAL)
float webMercatorT = textureCoordinates.y;
float encodedNormal = textureCoordAndEncodedNormals.z;
// 如果定义了 INCLUDE_WEB_MERCATOR_Y 宏,则使用 textureCoordAndEncodedNormals.z 作为 webMercatorT,
// encodedNormal 为 0.0
#elif defined(INCLUDE_WEB_MERCATOR_Y)
float webMercatorT = textureCoordAndEncodedNormals.z;
float encodedNormal = 0.0;
// 如果没有定义上述任何宏,则使用 textureCoordinates.y 作为 webMercatorT,encodedNormal 为 0.0
#else
float webMercatorT = textureCoordinates.y;
float encodedNormal = 0.0;
#endif
#endif
// 计算顶点在世界坐标系中和摄像机坐标系中的位置
vec3 position3DWC = position + u_center3D;
#ifdef GEODETIC_SURFACE_NORMALS
vec3 ellipsoidNormal = geodeticSurfaceNormal;
#else
vec3 ellipsoidNormal = normalize(position3DWC);
#endif
#if defined(EXAGGERATION) && defined(GEODETIC_SURFACE_NORMALS)
//获取夸张系数和相对高度
float exaggeration = u_terrainExaggerationAndRelativeHeight.x;
float relativeHeight = u_terrainExaggerationAndRelativeHeight.y;
//计算夸张后高度
float newHeight = (height - relativeHeight) * exaggeration + relativeHeight;
// stop from going through center of earth
//计算地球椭球体(ellipsoid)的最小半径,即 x 轴方向、y 轴方向和 z 轴方向的半径中的最小值,并赋给 minRadius 变量。
float minRadius = min(min(czm_ellipsoidRadii.x, czm_ellipsoidRadii.y), czm_ellipsoidRadii.z);
//将新的高度值 newHeight 与 -minRadius 进行比较,取其中较大的值,并重新赋给 newHeight 变量。这样做是为了确保地形不会穿过地球椭球体的中心。
newHeight = max(newHeight, -minRadius);
//计算高度变化的偏移量,通过将 ellipsoid 的法向量 ellipsoidNormal 乘以高度变化值 newHeight - height 来得到。
vec3 offset = ellipsoidNormal * (newHeight - height);
position += offset;
position3DWC += offset;
height = newHeight;
#endif
// 计算顶点在世界坐标系中和摄像机坐标系中的位置
gl_Position = getPosition(position, height, textureCoordinates);
// 计算顶点在摄像机坐标系中的位置,并传递给片段着色器
v_positionEC = (u_modifiedModelView * vec4(position, 1.0)).xyz;
// 计算顶点在模型坐标系中的位置,并传递给片段着色器
v_positionMC = position3DWC; // position in model coordinates
//// 传递纹理坐标信息和可能的地表几何表面法线信息给片段着色器
v_textureCoordinates = vec3(textureCoordinates, webMercatorT);
// 根据编译指令和是否使用光照计算等,传递其他需要的变量给片段着色器
#if defined(ENABLE_VERTEX_LIGHTING) || defined(GENERATE_POSITION_AND_NORMAL) || defined(APPLY_MATERIAL)
//使用 czm_octDecode 函数对压缩后的法线值 encodedNormal 进行解压缩,得到地形顶点的法线向量 normalMC。czm_octDecode 函数用于解压缩用八位字节存储的法线向量,将其转换为浮点数向量。
vec3 normalMC = czm_octDecode(encodedNormal);
#if defined(EXAGGERATION) && defined(GEODETIC_SURFACE_NORMALS)
//计算法线向量 normalMC 在椭球体法向量 ellipsoidNormal 方向上的投影,得到投影向量 projection。
vec3 projection = dot(normalMC, ellipsoidNormal) * ellipsoidNormal;
//计算法线向量 normalMC 与其在椭球体法向量 ellipsoidNormal 方向上的投影 projection 的差,得到拒绝向量 rejection
vec3 rejection = normalMC - projection;
//将投影向量 projection 与拒绝向量 rejection 的线性组合加权,通过地形扩展值 exaggeration 进行调整,并对结果进行归一化,得到新的法线向量 normalMC。
//这一步的目的是在启用地形高度扩展时,根据扩展值调整地形顶点的法线,以便更准确地模拟法线在高度变化时的表现。
normalMC = normalize(projection + rejection * exaggeration);
#endif
v_normalMC = normalMC;
//将模型坐标系中的法线向量转换为摄影机坐标系中的法线向量
v_normalEC = czm_normal3D * v_normalMC;
#endif
#if defined(FOG) || (defined(GROUND_ATMOSPHERE) && !defined(PER_FRAGMENT_GROUND_ATMOSPHERE))
bool dynamicLighting = false;
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_DAYNIGHT_SHADING) || defined(ENABLE_VERTEX_LIGHTING))
//如果条件满足(即启用了动态大气光照并且启用了昼夜光照或顶点光照)
dynamicLighting = true;
#endif
//检查是否启用了从太阳方向计算动态大气光照
//如果启用了 DYNAMIC_ATMOSPHERE_LIGHTING_FROM_SUN,则大气光照方向就是太阳方向;否则,它是光源方向(czm_lightDirectionWC)
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING_FROM_SUN)
vec3 atmosphereLightDirection = czm_sunDirectionWC;
#else
vec3 atmosphereLightDirection = czm_lightDirectionWC;
#endif
vec3 lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(position3DWC));
//用于计算大气散射效果
computeAtmosphereScattering(
position3DWC,
lightDirection,
v_atmosphereRayleighColor,
v_atmosphereMieColor,
v_atmosphereOpacity
);
#endif
#if defined(FOG) || defined(GROUND_ATMOSPHERE) || defined(UNDERGROUND_COLOR) || defined(TRANSLUCENT)
//该矩阵将顶点从世界坐标系转换到摄影机视图坐标系。最后,通过 length(...) 函数计算得到顶点到摄影机的距离,并将结果存储在变量 v_distance 中。
v_distance = length((czm_modelView3D * vec4(position3DWC, 1.0)).xyz);
#endif
#ifdef APPLY_MATERIAL
//这里获取了地球椭球体的北极坐标 czm_ellipsoidRadii.z,即椭球体的长轴半径
float northPoleZ = czm_ellipsoidRadii.z;
//定义了模型坐标系下的北极坐标 northPolePositionMC,它的 x 和 y 分量都为 0.0,z 分量为 northPoleZ,即北极的高度。
vec3 northPolePositionMC = vec3(0.0, 0.0, northPoleZ);
//在东北上坐标系中,通过被向量与上向量叉乘得到东向量
vec3 vectorEastMC = normalize(cross(northPolePositionMC - v_positionMC, ellipsoidNormal));
//计算地球椭球体法向量 ellipsoidNormal 与顶点的模型坐标系法向量 v_normalMC 的点积,并取绝对值
float dotProd = abs(dot(ellipsoidNormal, v_normalMC));
//计算 dotProd 的反余弦值并将结果存储在变量 v_slope 中。这个值代表了模型表面法线与椭球体法向量之间的夹角,用于后续的着色计算。
v_slope = acos(dotProd);
//将地球椭球体法向量 ellipsoidNormal 乘以 dotProd,得到被投影到椭球体法向量上的分量,并将结果存储在变量 normalRejected 中。
vec3 normalRejected = ellipsoidNormal * dotProd;
//计算模型坐标系下的法向量 v_normalMC 减去投影分量 normalRejected,得到被拒绝的分量,并将结果存储在变量 normalProjected 中。
vec3 normalProjected = v_normalMC - normalRejected;
//将被拒绝的分量 normalProjected 进行归一化得到向量 aspectVector,它代表了模型表面法线在椭球体法向量平面上的投影。
vec3 aspectVector = normalize(normalProjected);
//计算 aspectVector 和 vectorEastMC 之间的夹角,并将结果存储在变量 v_aspect 中。这个值用于后续的着色计算。即坡向
v_aspect = acos(dot(aspectVector, vectorEastMC));
float determ = dot(cross(vectorEastMC, aspectVector), ellipsoidNormal);
// 计算 `vectorEastMC` 和 `aspectVector` 的叉乘,得到一个新的向量,然后将这个向量与椭球体法向量 `ellipsoidNormal` 进行点积,得到浮点数 `determ`。
v_aspect = czm_branchFreeTernary(determ < 0.0, 2.0 * czm_pi - v_aspect, v_aspect);
// 使用 `czm_branchFreeTernary` 函数根据 `determ` 的值来更新 `v_aspect` 的值。
// 如果 `determ` 小于 0.0,则将 `v_aspect` 更新为 `2.0 * czm_pi - v_aspect`,否则保持 `v_aspect` 的原值不变。
// 这样确保 `v_aspect` 始终在一个确定的范围内,通常为 [0, 2π),用于表示模型表面的朝向角度。
v_height = height;
#endif
}
片元着色器
c++
uniform vec4 u_initialColor; // 初始颜色
// 纹理相关Uniform变量
#if TEXTURE_UNITS > 0
uniform sampler2D u_dayTextures[TEXTURE_UNITS];// 白天纹理
uniform vec4 u_dayTextureTranslationAndScale[TEXTURE_UNITS];// 纹理坐标偏移和缩放
uniform bool u_dayTextureUseWebMercatorT[TEXTURE_UNITS];// 是否使用WebMercator纹理坐标
// 以下为一些纹理特效的Uniform变量
#ifdef APPLY_ALPHA
uniform float u_dayTextureAlpha[TEXTURE_UNITS];// 透明度
#endif
#ifdef APPLY_DAY_NIGHT_ALPHA
uniform float u_dayTextureNightAlpha[TEXTURE_UNITS];// 夜晚纹理透明度
uniform float u_dayTextureDayAlpha[TEXTURE_UNITS];// 白天纹理透明度
#endif
#ifdef APPLY_SPLIT
uniform float u_dayTextureSplit[TEXTURE_UNITS];// 分割
#endif
#ifdef APPLY_BRIGHTNESS
uniform float u_dayTextureBrightness[TEXTURE_UNITS];// 亮度
#endif
#ifdef APPLY_CONTRAST
uniform float u_dayTextureContrast[TEXTURE_UNITS];// 对比度
#endif
#ifdef APPLY_HUE
uniform float u_dayTextureHue[TEXTURE_UNITS];// 色调
#endif
#ifdef APPLY_SATURATION
uniform float u_dayTextureSaturation[TEXTURE_UNITS];// 饱和度
#endif
#ifdef APPLY_GAMMA
uniform float u_dayTextureOneOverGamma[TEXTURE_UNITS]; // gamma校正
#endif
#ifdef APPLY_IMAGERY_CUTOUT
uniform vec4 u_dayTextureCutoutRectangles[TEXTURE_UNITS];// 纹理剪裁区域
#endif
#ifdef APPLY_COLOR_TO_ALPHA
uniform vec4 u_colorsToAlpha[TEXTURE_UNITS];// 颜色转为透明度
#endif
uniform vec4 u_dayTextureTexCoordsRectangle[TEXTURE_UNITS];// 纹理坐标矩形
#endif
// 高级渲染特效Uniform变量
#ifdef SHOW_REFLECTIVE_OCEAN
uniform sampler2D u_waterMask;// 水面蒙版纹理
uniform vec4 u_waterMaskTranslationAndScale;// 水面蒙版纹理坐标偏移和缩放
uniform float u_zoomedOutOceanSpecularIntensity; // 缩放水面镜面高光强度
#endif
#ifdef SHOW_OCEAN_WAVES
uniform sampler2D u_oceanNormalMap;// 水面法线贴图
#endif
// 大气和雾效果Uniform变量
#if defined(ENABLE_DAYNIGHT_SHADING) || defined(GROUND_ATMOSPHERE)
uniform vec2 u_lightingFadeDistance;// 光照淡入淡出距离
#endif
#ifdef TILE_LIMIT_RECTANGLE
uniform vec4 u_cartographicLimitRectangle;// 瓦片裁剪矩形
#endif
#ifdef GROUND_ATMOSPHERE
uniform vec2 u_nightFadeDistance; // 夜晚淡入淡出距离
#endif
#ifdef ENABLE_CLIPPING_PLANES
uniform highp sampler2D u_clippingPlanes;// 裁剪平面
uniform mat4 u_clippingPlanesMatrix;// 裁剪平面变换矩阵
uniform vec4 u_clippingPlanesEdgeStyle;// 裁剪平面边缘样式
#endif
#if defined(GROUND_ATMOSPHERE) || defined(FOG) && defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
uniform float u_minimumBrightness; // 最小亮度
#endif
#ifdef COLOR_CORRECT
uniform vec3 u_hsbShift; // Hue, saturation, brightness // 色调、饱和度和亮度偏移
#endif
#ifdef HIGHLIGHT_FILL_TILE
uniform vec4 u_fillHighlightColor;// 高亮填充瓦片的颜色
#endif
#ifdef TRANSLUCENT
uniform vec4 u_frontFaceAlphaByDistance;// 前表面透明度随距离变化
uniform vec4 u_backFaceAlphaByDistance;// 后表面透明度随距离变化
uniform vec4 u_translucencyRectangle;// 半透明矩形
#endif
#ifdef UNDERGROUND_COLOR
uniform vec4 u_undergroundColor;// 地下颜色
uniform vec4 u_undergroundColorAlphaByDistance; // 地下颜色透明度随距离变化
#endif
#ifdef ENABLE_VERTEX_LIGHTING
uniform float u_lambertDiffuseMultiplier;//Lambert 漫反射的乘数
#endif
varying vec3 v_positionMC;
varying vec3 v_positionEC;
varying vec3 v_textureCoordinates;
varying vec3 v_normalMC;
varying vec3 v_normalEC;
#ifdef APPLY_MATERIAL
varying float v_height;
varying float v_slope;//坡度
varying float v_aspect;//坡向
#endif
#if defined(FOG) || defined(GROUND_ATMOSPHERE) || defined(UNDERGROUND_COLOR) || defined(TRANSLUCENT)
varying float v_distance;//离相机距离
#endif
#if defined(GROUND_ATMOSPHERE) || defined(FOG)
varying vec3 v_atmosphereRayleighColor;//大气散射的颜色
varying vec3 v_atmosphereMieColor;//大气散射中的Mie散射颜色
varying float v_atmosphereOpacity;//大气的不透明度
#endif
//nearFarScalar:一个 vec4 类型的向量,包含四个元素:x 表示起始距离,y 表示起始值,z 表示结束距离,w 表示结束值。这些值用于定义插值范围。
//distance:一个 float 类型的输入距离,用于计算在 nearFarScalar 范围内的插值位置。
#if defined(UNDERGROUND_COLOR) || defined(TRANSLUCENT)
float interpolateByDistance(vec4 nearFarScalar, float distance)
{
float startDistance = nearFarScalar.x;
float startValue = nearFarScalar.y;
float endDistance = nearFarScalar.z;
float endValue = nearFarScalar.w;
float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);
return mix(startValue, endValue, t);
}
#endif
#if defined(UNDERGROUND_COLOR) || defined(TRANSLUCENT) || defined(APPLY_MATERIAL)
vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor)
{
//首先,函数从 sourceColor 中获取其RGB颜色分量,并使用 sourceColor.aaa 得到一个新的 vec3 向量,其中alpha值设置为1.0。这是为了在后续的计算中保持原始对象的不透明度。
//然后,对 destinationColor 执行以下计算:(1.0 - sourceColor.a),即目标颜色与源颜色的不透明度之差。
//接下来,将步骤1和步骤2的结果相加,即 sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a)。
//最后,返回混合后的颜色作为函数的结果。
return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a);
}
#endif
//该函数用于判断当前片元(像素)是否位于一个指定的矩形区域内。这通常用于在渲染透明效果时,确定是否需要应用透明的渲染方式。
#ifdef TRANSLUCENT
bool inTranslucencyRectangle()
{
return
v_textureCoordinates.x > u_translucencyRectangle.x &&
v_textureCoordinates.x < u_translucencyRectangle.z &&
v_textureCoordinates.y > u_translucencyRectangle.y &&
v_textureCoordinates.y < u_translucencyRectangle.w;
}
#endif
vec4 sampleAndBlend(
vec4 previousColor,
sampler2D textureToSample,
vec2 tileTextureCoordinates,
vec4 textureCoordinateRectangle,
vec4 textureCoordinateTranslationAndScale,
float textureAlpha,
float textureNightAlpha,
float textureDayAlpha,
float textureBrightness,
float textureContrast,
float textureHue,
float textureSaturation,
float textureOneOverGamma,
float split,
vec4 colorToAlpha,
float nightBlend)
{
// This crazy step stuff sets the alpha to 0.0 if this following condition is true:
// tileTextureCoordinates.s < textureCoordinateRectangle.s ||
// tileTextureCoordinates.s > textureCoordinateRectangle.p ||
// tileTextureCoordinates.t < textureCoordinateRectangle.t ||
// tileTextureCoordinates.t > textureCoordinateRectangle.q
// In other words, the alpha is zero if the fragment is outside the rectangle
// covered by this texture. Would an actual 'if' yield better performance?
//// 设置片元在纹理区域之外的alpha为0,实现裁剪效果
vec2 alphaMultiplier = step(textureCoordinateRectangle.st, tileTextureCoordinates);
textureAlpha = textureAlpha * alphaMultiplier.x * alphaMultiplier.y;
alphaMultiplier = step(vec2(0.0), textureCoordinateRectangle.pq - tileTextureCoordinates);
textureAlpha = textureAlpha * alphaMultiplier.x * alphaMultiplier.y;
// 根据夜晚混合值调整纹理alpha值(在定义了APPLY_DAY_NIGHT_ALPHA和ENABLE_DAYNIGHT_SHADING时生效)
#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
textureAlpha *= mix(textureDayAlpha, textureNightAlpha, nightBlend);
#endif
// 根据纹理坐标矩形的平移和缩放调整当前片元的纹理坐标
vec2 translation = textureCoordinateTranslationAndScale.xy;
vec2 scale = textureCoordinateTranslationAndScale.zw;
vec2 textureCoordinates = tileTextureCoordinates * scale + translation;
vec4 value = texture2D(textureToSample, textureCoordinates);
vec3 color = value.rgb;
float alpha = value.a;
// 如果定义了APPLY_COLOR_TO_ALPHA,则根据颜色与colorToAlpha的差异设置alpha值
#ifdef APPLY_COLOR_TO_ALPHA
vec3 colorDiff = abs(color.rgb - colorToAlpha.rgb);
colorDiff.r = max(max(colorDiff.r, colorDiff.g), colorDiff.b);
alpha = czm_branchFreeTernary(colorDiff.r < colorToAlpha.a, 0.0, alpha);
#endif
// 根据定义的选项对颜色进行处理
#if !defined(APPLY_GAMMA)
// 如果没有定义APPLY_GAMMA,则进行伽马矫正
vec4 tempColor = czm_gammaCorrect(vec4(color, alpha));
color = tempColor.rgb;
alpha = tempColor.a;
#else
// 否则,进行1/伽马校正
color = pow(color, vec3(textureOneOverGamma));
#endif
// 根据定义的选项对片元进行裁剪
#ifdef APPLY_SPLIT
float splitPosition = czm_splitPosition;
// Split to the left
if (split < 0.0 && gl_FragCoord.x > splitPosition) {
alpha = 0.0;
}
// Split to the right
else if (split > 0.0 && gl_FragCoord.x < splitPosition) {
alpha = 0.0;
}
#endif
// 根据定义的选项调整颜色亮度、对比度、色调和饱和度
#ifdef APPLY_BRIGHTNESS
color = mix(vec3(0.0), color, textureBrightness);
#endif
#ifdef APPLY_CONTRAST
color = mix(vec3(0.5), color, textureContrast);
#endif
#ifdef APPLY_HUE
color = czm_hue(color, textureHue);
#endif
#ifdef APPLY_SATURATION
color = czm_saturation(color, textureSaturation);
#endif
// 计算源alpha值和纹理alpha值的乘积
float sourceAlpha = alpha * textureAlpha;
// 计算最终输出的alpha值
float outAlpha = mix(previousColor.a, 1.0, sourceAlpha);
outAlpha += sign(outAlpha) - 1.0;
// 根据sourceAlpha混合前一颜色和当前颜色,以及最终输出的alpha值
vec3 outColor = mix(previousColor.rgb * previousColor.a, color, sourceAlpha) / outAlpha;
// When rendering imagery for a tile in multiple passes,
// some GPU/WebGL implementation combinations will not blend fragments in
// additional passes correctly if their computation includes an unmasked
// divide-by-zero operation,
// even if it's not in the output or if the output has alpha zero.
//
// For example, without sanitization for outAlpha,
// this renders without artifacts:
// if (outAlpha == 0.0) { outColor = vec3(0.0); }
//
// but using czm_branchFreeTernary will cause portions of the tile that are
// alpha-zero in the additional pass to render as black instead of blending
// with the previous pass:
// outColor = czm_branchFreeTernary(outAlpha == 0.0, vec3(0.0), outColor);
//
// So instead, sanitize against divide-by-zero,
// store this state on the sign of outAlpha, and correct on return.
// 确保在进行除法运算时不会出现除以零的情况,避免渲染错误
return vec4(outColor, max(outAlpha, 0.0));
}
// 颜色校正函数,对RGB颜色进行调整
vec3 colorCorrect(vec3 rgb) {
#ifdef COLOR_CORRECT
// 将RGB颜色转换为HSB颜色
vec3 hsb = czm_RGBToHSB(rgb);
// 进行HSB颜色偏移
hsb.x += u_hsbShift.x; // 色调
hsb.y = clamp(hsb.y + u_hsbShift.y, 0.0, 1.0); // 饱和度
hsb.z = hsb.z > czm_epsilon7 ? hsb.z + u_hsbShift.z : 0.0; // 亮度
// 将调整后的HSB颜色转换回RGB颜色
rgb = czm_HSBToRGB(hsb);
#endif
return rgb;
}
/*initialColor: 初始颜色,通常是纹理采样得到的颜色。
textureCoordinates: 片元的纹理坐标,用于在纹理上进行采样。
nightBlend: 夜晚混合值,用于根据时间调整颜色的白天和夜晚效果。
函数作用:该函数用于计算白天场景中的颜色。它可能使用纹理采样的颜色,然后根据夜晚混合值来调整颜色,以实现白天和夜晚过渡效果。*/
vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates, float nightBlend);
/*positionEyeCoordinates: 片元在眼空间中的位置坐标。
textureCoordinates: 片元的水体纹理坐标,用于在水体纹理上进行采样。
enuToEye: 一个3x3矩阵,用于将ENU坐标系(东-北-上)转换为眼空间坐标系。
imageryColor: 纹理采样得到的颜色。
specularMapValue: 镜面映射值,用于控制镜面高光的强度。
fade: 用于淡化效果的淡化值,通常是与水体边界相关的距离值。
函数作用:该函数用于计算水体的颜色,通常用于水体渲染。它可能使用纹理采样的颜色,并结合其他参数(如镜面映射值、淡化值等)来计算最终的水体颜色。水体的着色通常需要考虑视角、反射等特殊效果,因此这个函数可能会进行复杂的计算以获得逼真的水体外观。*/
vec4 computeWaterColor(vec3 positionEyeCoordinates, vec2 textureCoordinates, mat3 enuToEye, vec4 imageryColor, float specularMapValue, float fade);
const float fExposure = 2.0;// 曝光度(Exposure)值
// 计算椭球体表面上的点的位置
vec3 computeEllipsoidPosition()
{
// 计算当前视口下的每像素米数(Meters Per Pixel),用于后面的射线计算
float mpp = czm_metersPerPixel(vec4(0.0, 0.0, -czm_currentFrustum.x, 1.0), 1.0);
// 计算当前片元在视口中的标准化坐标(范围[-1, 1]),并将其转换为视口下的像素坐标
vec2 xy = gl_FragCoord.xy / czm_viewport.zw * 2.0 - vec2(1.0);
xy *= czm_viewport.zw * mpp * 0.5;
// 计算片元在视口空间中的方向向量,这个向量指向从相机位置出发经过该片元的射线方向
vec3 direction = normalize(vec3(xy, -czm_currentFrustum.x));
// 构建一条射线,从相机位置出发,沿着片元方向向量
czm_ray ray = czm_ray(vec3(0.0), direction);
// 获取椭球体的中心位置和反向半径(inverse radii)
vec3 ellipsoid_center = czm_view[3].xyz;
// 计算射线与椭球体相交的区间
czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid_center, czm_ellipsoidInverseRadii);
// 根据相交区间,计算射线与椭球体表面上的交点位置
vec3 ellipsoidPosition = czm_pointAlongRay(ray, intersection.start);
// 将交点位置从视图空间转换回世界空间
return (czm_inverseView * vec4(ellipsoidPosition, 1.0)).xyz;
}
void main()
{
//不在范围内的不绘制
#ifdef TILE_LIMIT_RECTANGLE
if (v_textureCoordinates.x < u_cartographicLimitRectangle.x || u_cartographicLimitRectangle.z < v_textureCoordinates.x ||
v_textureCoordinates.y < u_cartographicLimitRectangle.y || u_cartographicLimitRectangle.w < v_textureCoordinates.y)
{
discard;
}
#endif
// 声明一个变量用于存储裁剪距离
#ifdef ENABLE_CLIPPING_PLANES
// 调用clip函数,计算片元到各个裁剪平面的距离,并取最小的一个距离值
float clipDistance = clip(gl_FragCoord, u_clippingPlanes, u_clippingPlanesMatrix);
#endif
#if defined(SHOW_REFLECTIVE_OCEAN) || defined(ENABLE_DAYNIGHT_SHADING) || defined(HDR)
//计算了表面的法线向量,并将其从模型坐标系转换到了眼睛坐标系。
vec3 normalMC = czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)); // normalized surface normal in model coordinates
vec3 normalEC = czm_normal3D * normalMC; // normalized surface normal in eye coordiantes
#endif
#if defined(APPLY_DAY_NIGHT_ALPHA) && defined(ENABLE_DAYNIGHT_SHADING)
//用于在白天和夜晚之间混合颜色。具体计算方式是通过调用 czm_getLambertDiffuse 函数计算漫反射光照强度,
//然后用 5.0 乘以该强度,并通过 clamp 函数将其限制在 0.0 和 1.0 之间。再用 1.0 减去这个值,得到最终的 nightBlend。
float nightBlend = 1.0 - clamp(czm_getLambertDiffuse(czm_lightDirectionEC, normalEC) * 5.0, 0.0, 1.0);
#else
float nightBlend = 0.0;
#endif
// The clamp below works around an apparent bug in Chrome Canary v23.0.1241.0
// where the fragment shader sees textures coordinates < 0.0 and > 1.0 for the
// fragments on the edges of tiles even though the vertex shader is outputting
// coordinates strictly in the 0-1 range.
//计算白天的颜色
vec4 color = computeDayColor(u_initialColor, clamp(v_textureCoordinates, 0.0, 1.0), nightBlend);
//通过条件判断,检查当前片段的纹理坐标 v_textureCoordinates 是否处于瓦片的边界上。这里的 (1.0/256.0) 和 (255.0/256.0) 分别代表了纹理坐标的最小值和最大值,以保留一个像素的边缘。
//若纹理坐标位于该范围之外,则表示该片段位于瓦片的边界上。
//如果纹理坐标在瓦片的边界上,则将 color 设置为红色,即 vec4(1.0, 0.0, 0.0, 1.0),表示将该片段的颜色显示为红色。
#ifdef SHOW_TILE_BOUNDARIES
if (v_textureCoordinates.x < (1.0/256.0) || v_textureCoordinates.x > (255.0/256.0) ||
v_textureCoordinates.y < (1.0/256.0) || v_textureCoordinates.y > (255.0/256.0))
{
color = vec4(1.0, 0.0, 0.0, 1.0);
}
#endif
// 这段代码计算了一个名为 'fade' 的值,用于控制日夜渐变或大气渐变效果。
#if defined(ENABLE_DAYNIGHT_SHADING) || defined(GROUND_ATMOSPHERE)
float cameraDist;
// 首先,根据当前场景模式不同,计算摄像机到椭球体表面的距离。
// 对于 2D 模式,使用近平面和远平面之间距离的一半作为摄像机距离 cameraDist。
// 对于 Columbus View 模式,使用摄像机的负高度作为摄像机距离 cameraDist。
// 对于 3D 模式,使用摄像机位置向量的长度作为摄像机距离 cameraDist。
if (czm_sceneMode == czm_sceneMode2D)
{
cameraDist = max(czm_frustumPlanes.x - czm_frustumPlanes.y, czm_frustumPlanes.w - czm_frustumPlanes.z) * 0.5;
}
else if (czm_sceneMode == czm_sceneModeColumbusView)
{
cameraDist = -czm_view[3].z;
}
else
{
cameraDist = length(czm_view[3]);
}
// 然后,根据用户设置的 'u_lightingFadeDistance',计算渐变的起始距离和结束距离。
// 对于非 3D 场景,减去最大椭球体半径以适应场景。
// fadeOutDist 表示淡出距离,fadeInDist 表示淡入距离。
float fadeOutDist = u_lightingFadeDistance.x;
float fadeInDist = u_lightingFadeDistance.y;
if (czm_sceneMode != czm_sceneMode3D) {
vec3 radii = czm_ellipsoidRadii;
float maxRadii = max(radii.x, max(radii.y, radii.z));
fadeOutDist -= maxRadii;
fadeInDist -= maxRadii;
}
// 最后,根据摄像机距离在渐变范围内的位置,计算 'fade' 值,并将其限制在区间 [0.0, 1.0] 内。
// 'fade' 值在混合和计算最终颜色时被使用,用于根据相机距离调整特效的透明度和出现效果。
float fade = clamp((cameraDist - fadeOutDist) / (fadeInDist - fadeOutDist), 0.0, 1.0);
#else
float fade = 0.0;
#endif
#ifdef SHOW_REFLECTIVE_OCEAN
// 首先,根据用户设置的 'u_waterMaskTranslationAndScale',计算水体遮罩纹理的坐标。
// 通过乘以缩放因子和加上平移量来映射 'v_textureCoordinates' 至水体遮罩纹理坐标空间。
// 这里的纹理坐标有可能超出 [0.0, 1.0] 范围,所以需要将 y 轴坐标反转,以适应纹理坐标的约定。
vec2 waterMaskTranslation = u_waterMaskTranslationAndScale.xy;
vec2 waterMaskScale = u_waterMaskTranslationAndScale.zw;
vec2 waterMaskTextureCoordinates = v_textureCoordinates.xy * waterMaskScale + waterMaskTranslation;
// 这里的纹理坐标有可能超出 [0.0, 1.0] 范围,所以需要将 y 轴坐标反转,以适应纹理坐标的约定。
waterMaskTextureCoordinates.y = 1.0 - waterMaskTextureCoordinates.y;
// 接着,从水体遮罩纹理中采样出一个浮点数 'mask'。
// 如果 'mask' 大于 0.0,说明当前像素位于水体遮罩的非透明部分,即水体表面。
float mask = texture2D(u_waterMask, waterMaskTextureCoordinates).r;
if (mask > 0.0)
{
//东北天坐标系(ENU)转换为眼睛坐标系(EC)。
mat3 enuToEye = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalEC);
//计算椭球体表面的纹理坐标,分别得到原始纹理坐标 'ellipsoidTextureCoordinates' 和反转的纹理坐标 'ellipsoidFlippedTextureCoordinates'。
// 这些纹理坐标用于根据表面法线 'normalMC' 在椭球体表面上进行采样。
vec2 ellipsoidTextureCoordinates = czm_ellipsoidWgs84TextureCoordinates(normalMC);
vec2 ellipsoidFlippedTextureCoordinates = czm_ellipsoidWgs84TextureCoordinates(normalMC.zyx);
// 使用 'czm_morphTime' 来在原始纹理坐标和反转的纹理坐标之间进行插值。
// 'czm_morphTime' 表示椭球体表面的变形程度,用于实现水体的波动效果。
// 然后,使用 'smoothstep' 函数将 'normalMC.z'(表面法线的 Z 分量)映射到 [0.9, 0.95] 范围,
// 以限制波动的变形区间,再与 'czm_morphTime' 做插值,得到最终纹理坐标 'textureCoordinates'。
vec2 textureCoordinates = mix(ellipsoidTextureCoordinates, ellipsoidFlippedTextureCoordinates, czm_morphTime * smoothstep(0.9, 0.95, normalMC.z));
// 最后,调用 'computeWaterColor' 函数来计算水体的颜色。
// 'computeWaterColor' 函数接受一些参数,包括视点坐标 'v_positionEC',纹理坐标 'textureCoordinates',
// 以及之前计算得到的 'enuToEye' 矩阵,颜色 'color',水体遮罩值 'mask' 和之前计算得到的 'fade' 值。
// 这个函数将根据输入的参数计算水体的颜色,并更新 'color' 的值。
color = computeWaterColor(v_positionEC, textureCoordinates, enuToEye, color, mask, fade);
}
#endif
#ifdef APPLY_MATERIAL
czm_materialInput materialInput;
materialInput.st = v_textureCoordinates.st;
materialInput.normalEC = normalize(v_normalEC);
materialInput.positionToEyeEC = -v_positionEC;
materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalize(v_normalEC));
materialInput.slope = v_slope;
materialInput.height = v_height;
materialInput.aspect = v_aspect;
czm_material material = czm_getMaterial(materialInput);
vec4 materialColor = vec4(material.diffuse, material.alpha);
color = alphaBlend(materialColor, color);
#endif
#ifdef ENABLE_VERTEX_LIGHTING
//首先,计算光照强度 'diffuseIntensity',使用 Lambert 光照模型来计算光照强度。
// 'czm_getLambertDiffuse(czm_lightDirectionEC, normalize(v_normalEC)) 计算当前像素的 Lambert 漫反射强度,
//'czm_lightDirectionEC' 是光源方向在视点坐标系中的表示,'normalize(v_normalEC)' 是当前像素的视点坐标系中的法线方向。
// 'u_lambertDiffuseMultiplier' 是一个用户定义的参数,用于调整漫反射的强度。
// 'clamp' 函数将光照强度限制在 [0.0, 1.0] 范围内,避免出现过亮或过暗的情况。
// '0.3' 是一个常数偏移量,用于调整光照的亮度。
float diffuseIntensity = clamp(czm_getLambertDiffuse(czm_lightDirectionEC, normalize(v_normalEC)) * u_lambertDiffuseMultiplier + 0.3, 0.0, 1.0);
vec4 finalColor = vec4(color.rgb * czm_lightColor * diffuseIntensity, color.a);
#elif defined(ENABLE_DAYNIGHT_SHADING)
float diffuseIntensity = clamp(czm_getLambertDiffuse(czm_lightDirectionEC, normalEC) * 5.0 + 0.3, 0.0, 1.0);
diffuseIntensity = mix(1.0, diffuseIntensity, fade);
//'mix' 函数根据 'fade' 将光照强度从 1.0(白天)线性插值到 'diffuseIntensity'(夜晚)。
vec4 finalColor = vec4(color.rgb * czm_lightColor * diffuseIntensity, color.a);
#else
vec4 finalColor = color;
#endif
#ifdef ENABLE_CLIPPING_PLANES
vec4 clippingPlanesEdgeColor = vec4(1.0);
clippingPlanesEdgeColor.rgb = u_clippingPlanesEdgeStyle.rgb;
float clippingPlanesEdgeWidth = u_clippingPlanesEdgeStyle.a;
//通过比较当前像素到裁剪平面的距离 'clipDistance' 与裁剪平面边缘宽度 'clippingPlanesEdgeWidth',
// 来判断当前像素是否位于裁剪平面边缘范围内。
if (clipDistance < clippingPlanesEdgeWidth)
{
finalColor = clippingPlanesEdgeColor;
}
#endif
#ifdef HIGHLIGHT_FILL_TILE
finalColor = vec4(mix(finalColor.rgb, u_fillHighlightColor.rgb, u_fillHighlightColor.a), finalColor.a);
#endif
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING_FROM_SUN)
vec3 atmosphereLightDirection = czm_sunDirectionWC;
#else
vec3 atmosphereLightDirection = czm_lightDirectionWC;
#endif
// 如果启用了GROUND_ATMOSPHERE或FOG,则进行处理
#if defined(GROUND_ATMOSPHERE) || defined(FOG)
// 如果不是背面片元(即观察者背对着面片),则进行处理
if (!czm_backFacing())
{
// dynamicLighting标记是否启用了动态大气光照计算
bool dynamicLighting = false;
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_DAYNIGHT_SHADING) || defined(ENABLE_VERTEX_LIGHTING))
dynamicLighting = true;
#endif
vec3 rayleighColor;// 雷利散射颜色
vec3 mieColor;// 米氏散射颜色
float opacity;// 大气层透明度
vec3 positionWC;// 片元世界坐标
vec3 lightDirection;// 大气光照方向
// 当相机距离远(camera distance > nightFadeOutDistance)时,将在片元着色器中计算散射效果。
// 否则,在顶点着色器中计算散射效果。
#ifdef PER_FRAGMENT_GROUND_ATMOSPHERE
// 计算片元世界坐标
positionWC = computeEllipsoidPosition();
// 选择大气光照方向
lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
// 计算大气散射效果(片元着色器计算)
computeAtmosphereScattering(
positionWC,
lightDirection,
rayleighColor,
mieColor,
opacity
);
#else
// 使用顶点着色器中已计算的散射效果(避免重复计算)
positionWC = v_positionMC;
lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
rayleighColor = v_atmosphereRayleighColor;
mieColor = v_atmosphereMieColor;
opacity = v_atmosphereOpacity;
#endif
// 调整散射颜色(修复颜色偏移)
rayleighColor = colorCorrect(rayleighColor);
mieColor = colorCorrect(mieColor);
// 计算地面大气颜色
vec4 groundAtmosphereColor = computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
// 若启用FOG,则对选定的雾区域(接近地球)应用雾效果
#ifdef FOG
// 将雾的颜色设置为地面大气颜色
vec3 fogColor = groundAtmosphereColor.rgb;
// 若存在光照,则将光照应用于雾效果
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
// 计算雾的降低量,根据视角方向与大气光照方向的夹角
float darken = clamp(dot(normalize(czm_viewerPositionWC), atmosphereLightDirection), u_minimumBrightness, 1.0);
// 降低雾颜色的亮度
fogColor *= darken;
#endif
// 根据配置应用后处理,如ACES色调映射、色彩校正等
#ifndef HDR
fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
fogColor.rgb = czm_inverseGamma(fogColor.rgb);
#endif
// 使用czm_fog函数计算雾效果并混合到最终颜色
const float modifier = 0.15;
finalColor = vec4(czm_fog(v_distance, finalColor.rgb, fogColor.rgb, modifier), finalColor.a);
#else
// The transmittance is based on optical depth i.e. the length of segment of the ray inside the atmosphere.
// This value is larger near the "circumference", as it is further away from the camera. We use it to
// brighten up that area of the ground atmosphere.
// 根据大气光照亮度调整地面大气颜色
// 这里的代码根据相机与地面的距离调整亮度(夜晚淡出效果)
const float transmittanceModifier = 0.5;
float transmittance = transmittanceModifier + clamp(1.0 - groundAtmosphereColor.a, 0.0, 1.0);
vec3 finalAtmosphereColor = finalColor.rgb + groundAtmosphereColor.rgb * transmittance;
// 若启用DYNAMIC_ATMOSPHERE_LIGHTING且ENABLE_VERTEX_LIGHTING或ENABLE_DAYNIGHT_SHADING,则计算光照
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
float fadeInDist = u_nightFadeDistance.x;
float fadeOutDist = u_nightFadeDistance.y;
// 混合大气光照和地面大气颜色,根据太阳光线照射强度和视角方向调整
// 计算太阳光线照射强度
float sunlitAtmosphereIntensity = clamp((cameraDist - fadeOutDist) / (fadeInDist - fadeOutDist), 0.05, 1.0);
// 计算光照强度,以视角方向与大气光照方向的点积为基准
float darken = clamp(dot(normalize(positionWC), atmosphereLightDirection), 0.0, 1.0);
// 混合地面大气颜色和最终颜色
vec3 darkenendGroundAtmosphereColor = mix(groundAtmosphereColor.rgb, finalAtmosphereColor.rgb, darken);
finalAtmosphereColor = mix(darkenendGroundAtmosphereColor, finalAtmosphereColor, sunlitAtmosphereIntensity);
#endif
// 根据配置对最终颜色进行后处理,如ACES色调映射、色彩校正等
#ifndef HDR
finalAtmosphereColor.rgb = vec3(1.0) - exp(-fExposure * finalAtmosphereColor.rgb);
#else
finalAtmosphereColor.rgb = czm_saturation(finalAtmosphereColor.rgb, 1.6);
#endif
// 将地面大气颜色与最终颜色进行混合,根据fade参数调整混合程度
finalColor.rgb = mix(finalColor.rgb, finalAtmosphereColor.rgb, fade);
#endif
}
#endif
#ifdef UNDERGROUND_COLOR
//首先,通过 'czm_backFacing()' 函数判断当前片元是否为背面片元,即不可见的片元。
if (czm_backFacing())
{
//计算当前片元到椭球体表面的距离 'distanceFromEllipsoid',保证距离为非负值,使用 'czm_eyeHeight' 函数获取相机到椭球体表面的距离。
float distanceFromEllipsoid = max(czm_eyeHeight, 0.0);
//计算片元到当前几何体(例如地形)的距离 'distance',保证距离为非负值,使用 'max' 函数防止距离为负。
float distance = max(v_distance - distanceFromEllipsoid, 0.0);
// 根据片元到几何体的距离 'distance',使用 'interpolateByDistance' 函数来获取一个融合因子 'blendAmount'。
// 'u_undergroundColorAlphaByDistance' 参数是一个 vec4,表示着色器根据距离插值 'u_undergroundColor' 颜色的范围和透明度。
// 'blendAmount' 是一个介于 0.0 和 1.0 之间的值,表示距离越远,融合透明度越低。
float blendAmount = interpolateByDistance(u_undergroundColorAlphaByDistance, distance);
vec4 undergroundColor = vec4(u_undergroundColor.rgb, u_undergroundColor.a * blendAmount);
finalColor = alphaBlend(undergroundColor, finalColor);
}
#endif
#ifdef TRANSLUCENT
if (inTranslucencyRectangle())
{
// 1. 首先,根据片元是否为前面片元 'gl_FrontFacing',选择对应的 alpha 插值参数
vec4 alphaByDistance = gl_FrontFacing ? u_frontFaceAlphaByDistance : u_backFaceAlphaByDistance;
//使用 'interpolateByDistance' 函数根据片元到相机的距离 'v_distance',来获取一个 alpha 插值因子
//这将使得在半透明矩形内的片元透明度受距离影响,距离越远,透明度越低
finalColor.a *= interpolateByDistance(alphaByDistance, v_distance);
}
#endif
gl_FragColor = finalColor;
}
#ifdef SHOW_REFLECTIVE_OCEAN
float waveFade(float edge0, float edge1, float x)
{
//edge0 和 edge1 是渐变的起始点和结束点。
//x 是需要计算渐变因子的位置。
//首先,通过 (x - edge0) / (edge1 - edge0) 计算出一个范围在 [0, 1] 之间的比例 y。
//然后,使用 pow(1.0 - y, 5.0) 计算 1 - y 的五次方,将 y 的值逐渐变小,从而产生类似波浪形的渐变效果。
float y = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return pow(1.0 - y, 5.0);
}
//直接通过 (x - edge0) / (edge1 - edge0) 计算出一个范围在 [0, 1] 之间的比例,并使用 clamp 函数将其限制在 [0, 1] 范围内,以确保渐变因子始终在有效的范围内。
float linearFade(float edge0, float edge1, float x)
{
return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
}
// Based on water rendering by Jonas Wagner:
// http://29a.ch/2012/7/19/webgl-terrain-rendering-water-fog
// low altitude wave settings
//oceanFrequencyLowAltitude 控制低海拔条件下波浪的频率。频率较低意味着波浪的波长较长。
//oceanAnimationSpeedLowAltitude 控制低海拔条件下波浪的动画速度。速度较慢意味着波浪动画的变化较为缓慢。
//oceanOneOverAmplitudeLowAltitude 控制低海拔条件下波浪的振幅。振幅较大意味着波浪的高度变化范围较大。
//oceanSpecularIntensity 控制低海拔条件下波浪的高光强度。较大的值意味着波浪的高光更加明亮。
const float oceanFrequencyLowAltitude = 825000.0;
const float oceanAnimationSpeedLowAltitude = 0.004;
const float oceanOneOverAmplitudeLowAltitude = 1.0 / 2.0;
const float oceanSpecularIntensity = 0.5;
// high altitude wave settings
const float oceanFrequencyHighAltitude = 125000.0;
const float oceanAnimationSpeedHighAltitude = 0.008;
const float oceanOneOverAmplitudeHighAltitude = 1.0 / 2.0;
vec4 computeWaterColor(
vec3 positionEyeCoordinates, // 相机视角下的水体位置坐标(眼坐标系)
vec2 textureCoordinates, // 水体纹理坐标
mat3 enuToEye, // 坐标转换矩阵(ENU到眼坐标系)
vec4 imageryColor, // 图像颜色(纹理采样得到的颜色)
float maskValue, // 控制水体遮罩的值
float fade // 控制水体褪色的值
)
{
// 计算相机视角到水体的距离
vec3 positionToEyeEC = -positionEyeCoordinates;
float positionToEyeECLength = length(positionToEyeEC);
// 修复在Android设备上Firefox浏览器中的一个bug
vec3 normalizedPositionToEyeEC = normalize(normalize(positionToEyeEC));
// 根据相机距离调整波浪强度
float waveIntensity = waveFade(70000.0, 1000000.0, positionToEyeECLength);
#ifdef SHOW_OCEAN_WAVES
// 高海拔波浪
float time = czm_frameNumber * oceanAnimationSpeedHighAltitude;
vec4 noise = czm_getWaterNoise(u_oceanNormalMap, textureCoordinates * oceanFrequencyHighAltitude, time, 0.0);
vec3 normalTangentSpaceHighAltitude = vec3(noise.xy, noise.z * oceanOneOverAmplitudeHighAltitude);
// 低海拔波浪
time = czm_frameNumber * oceanAnimationSpeedLowAltitude;
noise = czm_getWaterNoise(u_oceanNormalMap, textureCoordinates * oceanFrequencyLowAltitude, time, 0.0);
vec3 normalTangentSpaceLowAltitude = vec3(noise.xy, noise.z * oceanOneOverAmplitudeLowAltitude);
// 根据距离混合两个波浪层
float highAltitudeFade = linearFade(0.0, 60000.0, positionToEyeECLength);
float lowAltitudeFade = 1.0 - linearFade(20000.0, 60000.0, positionToEyeECLength);
vec3 normalTangentSpace =
(highAltitudeFade * normalTangentSpaceHighAltitude) +
(lowAltitudeFade * normalTangentSpaceLowAltitude);
normalTangentSpace = normalize(normalTangentSpace);
// 随着远离水面,淡出波浪的扰动效果
normalTangentSpace.xy *= waveIntensity;
normalTangentSpace = normalize(normalTangentSpace);
#else
vec3 normalTangentSpace = vec3(0.0, 0.0, 1.0);
#endif
// 将切线空间的法线转换为眼坐标系下的法线
vec3 normalEC = enuToEye * normalTangentSpace;
// 波浪高光颜色
const vec3 waveHighlightColor = vec3(0.3, 0.45, 0.6);
// 使用漫反射光照突出显示波浪
float diffuseIntensity = czm_getLambertDiffuse(czm_lightDirectionEC, normalEC) * maskValue;
vec3 diffuseHighlight = waveHighlightColor * diffuseIntensity * (1.0 - fade);
#ifdef SHOW_OCEAN_WAVES
// 在漫反射光照较低或不存在的区域,仅使用波浪的凹凸程度突出显示,而不考虑特定的光照方向
float tsPerturbationRatio = normalTangentSpace.z;
vec3 nonDiffuseHighlight = mix(waveHighlightColor * 5.0 * (1.0 - tsPerturbationRatio), vec3(0.0), diffuseIntensity);
#else
vec3 nonDiffuseHighlight = vec3(0.0);
#endif
// 在3D模式和放大时添加镜面高光
float specularIntensity = czm_getSpecular(czm_lightDirectionEC, normalizedPositionToEyeEC, normalEC, 10.0);
float surfaceReflectance = mix(0.0, mix(u_zoomedOutOceanSpecularIntensity, oceanSpecularIntensity, waveIntensity), maskValue);
float specular = specularIntensity * surfaceReflectance;
#ifdef HDR
specular *= 1.4;
float e = 0.2;
float d = 3.3;
float c = 1.7;
// HDR效果,应用曝光和色调映射
vec3 color = imageryColor.rgb + (c * (vec3(e) + imageryColor.rgb * d) * (diffuseHighlight + nonDiffuseHighlight + specular));
#else
// 非HDR效果,直接叠加颜色高光
vec3 color = imageryColor.rgb + diffuseHighlight + nonDiffuseHighlight + specular;
#endif
return vec4(color, imageryColor.a);
}
#endif // #ifdef SHOW_REFLECTIVE_OCEAN