前言
之前给大家分享过如何实现Cesium下雪效果和Cesium雾气效果,都是通过后处理、粒子系统实现三维场景的天气氛围仿真。 今天继续分享Cesium天气特效系列 ------ 自定义黄昏日落氛围效果。
同样采用 PostProcessStage 后处理着色器 实现,支持:
- 一键开启 / 关闭黄昏效果。
- Slider 滑块实时调节黄昏浓烈程度。
- 远景分层暖雾渐变,画面更有日落层次感。
可以直接用于数字孪生、三维场景氛围美化、环境仿真场景。
演示动态图示如下:

演示地址如下:
效果特点:
- 区别于简单的全局变色,采用屏幕竖向分层渲染,天空远景黄昏暖色更浓,近景地形建筑更自然。
- 通过
uniform对外暴露强度参数,支持滑块实时动态调节。
实现思路:
通过 Cesium 后处理管线,自定义片元着色器:
- 对原始场景纹理进行采样。
- 整体压暗模拟傍晚光线减弱。
- 叠加暖橙日落色调。
- 根据屏幕纵向坐标实现远景分层暖雾效果。
- 外部绑定滑块数值,动态控制黄昏混合强度。
完整代码
1. template 结构:
xml
<template>
<div class="main">
<!-- 地球容器 -->
<div class="content" ref="content" id="earth"></div>
<div class="btn-border" v-if="isLoading">
<div class="slider-border" v-if="isDusk">
<div class="slider-label">黄昏浓烈程度:{{ Number(duskStrength).toFixed(2) }}</div>
<!-- 黄昏浓度滑块容器,用于控制黄昏浓烈程度,仅开启黄昏效果时有效 -->
<el-slider
class="dusk-slider"
v-model.number="duskStrength"
:min="0"
:max="0.4"
:step="0.01"
@input="updateDuskStrength"
/>
</div>
<!-- 镜头复位按钮:一键飞回到初始视角 -->
<el-button type="primary" size="default" class="btn" @click="flyTo">初始位置</el-button>
<!-- 黄昏效果开关按钮:切换开启/关闭状态变量isDusk,根据isDusk状态动态修改按钮文字 -->
<el-button type="primary" size="default" class="btn" @click="duskControl">
{{ isDusk ? '关闭黄昏效果' : '开启黄昏效果' }}
</el-button>
</div>
<!-- 加载提示文字,地图未初始化完成时显示 -->
<div class="loading" v-if="!isLoading">Loading...</div>
</div>
</template>
2. script 代码:
ini
<script setup>
import { onMounted, nextTick, ref, onUnmounted } from 'vue';
import { token } from '../../utils/common.js';
// 黄昏效果全局开关标识 true=开启,false=关闭
const isDusk = ref(false);
// 黄昏浓烈程度默认值0.4,滑块范围0~0.4
const duskStrength = ref(0.4);
// 地图加载完成标识 false=加载中 true=加载完毕
const isLoading = ref(false);
let myMar = null;
onUnmounted(() => {
// 若黄昏后处理实例存在,移除并销毁
if (isDusk.value && window.duskStage) {
window.viewer.scene.postProcessStages.remove(window.duskStage);
window.duskStage = null;
isDusk.value = false;
}
// 销毁 Cesium Viewer 实例,释放资源
if (window.viewer) {
window.viewer.destroy();
window.viewer = null;
}
if (myMar) {
clearTimeout(myMar);
myMar = null;
}
});
// 组件挂载后:初始化地图
onMounted(() => {
nextTick(() => {
initMap();
});
});
// 初始化 Cesium 地图的方法
const initMap = async () => {
// 设置 Cesium Ion 的token(这里替换成您的Cesium Ion token)
Cesium.Ion.defaultAccessToken = token;
// 设置默认视角范围(中国区域)
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(89.5, 20.4, 110.4, 61.2);
// 异步加载Cesium官方全球地形服务
const terrainProvider = await Cesium.createWorldTerrainAsync({
requestWaterMask: true,
requestVertexNormals: true // 开启地形顶点法线,光影明暗更真实,适配黄昏滤镜
});
// 创建 Viewer 实例
window.viewer = new Cesium.Viewer('earth', {
terrainProvider: terrainProvider, // 绑定全球地形
animation: false, // 关闭动画控件
timeline: false, // 关闭时间轴
infoBox: false, // 关闭信息框
geocoder: false, // 关闭地理编码搜索
homeButton: false, // 关闭主页按钮
sceneModePicker: false, // 关闭场景模式切换
baseLayerPicker: false, // 关闭底图选择器
navigationHelpButton: false, // 关闭导航帮助
fullscreenButton: false, // 关闭全屏按钮
selectionIndicator: false, // 关闭选择指示器
shouldAnimate: false, // 关闭自动播放动画
contextOptions: { // WebGL 上下文配置
webgl: {
powerPreference: "high-performance", // 高性能模式
preserveDrawingBuffer: false // 不保留绘图缓冲(节省内存)
}
}
});
// 设置模拟时间(可选,用于光影效果)
Cesium.JulianDate.fromDate(new Date('2026/05/02 17:30:00'));
myMar = setTimeout(() => {
isLoading.value = true;
flyTo();
}, 3000);
};
// 飞往初始视角
const flyTo = () => {
window.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(117.7028548405434, 26.046540824114697, 1341.492305543057),
orientation: {
heading: Cesium.Math.toRadians(38.298412435906), // 航向角
pitch: Cesium.Math.toRadians(-4.009058145409306), // 俯仰角
roll: Cesium.Math.toRadians(0.0010907956242872084) // 翻滚角
},
duration: 6 // 飞行时长6秒
});
};
// 滑块拖动实时更新黄昏强度的方法
const updateDuskStrength = (val) => {
if (window.duskStage) { // 校验黄昏处理实例是否存在
// 将滑块数值传递给着色器uniform变量,实时更新画面黄昏浓度
window.duskStage.uniforms.duskStrength = Number(val);
}
};
/**
* 黄昏效果开关核心方法
* 关闭:移除后处理管线、销毁着色器实例
* 开启:创建黄昏片元着色器,注册到场景后处理渲染管线
*/
const duskControl = () => {
if (isDusk.value) { // 当黄昏为开启状态时,执行关闭黄昏的逻辑
// 从渲染管线移除黄昏滤镜,释放WebGL资源
window.viewer.scene.postProcessStages.remove(window.duskStage);
window.duskStage = null;
isDusk.value = false;
} else {
// 黄昏效果片元着色器代码
const DuskShader = `
// Cesium后处理内置完整场景纹理
uniform sampler2D colorTexture;
// 外部滑块传入黄昏浓烈程度 0~0.4
uniform float duskStrength;
// 当前像素屏幕纹理坐标 [0,1]
in vec2 v_textureCoordinates;
// 输出叠加黄昏后的像素RGBA
out vec4 fragColor;
void main(void) {
// 采样原始场景画面
vec4 originColor = texture(colorTexture, v_textureCoordinates);
vec3 rawRGB = originColor.rgb;
// 黄昏基础色:暖橙红 日落色调
vec3 duskWarmColor = vec3(0.98, 0.72, 0.42);
// 黄昏压暗系数,模拟傍晚光线减弱
float darkFactor = 1.0 - duskStrength * 0.3;
// 垂直分层:画面顶部远景黄昏雾更重
float screenY = v_textureCoordinates.y;
float warmFogFactor = smoothstep(0.15, 1.0, screenY) * duskStrength * 0.6;
// 第一步:原图整体压暗,模拟傍晚弱光
vec3 darkBase = rawRGB * darkFactor;
// 第二步:叠加暖橙黄昏底色
vec3 mixWarm = mix(darkBase, duskWarmColor, duskStrength * 0.45);
// 第三步:远景叠加更浓的暖色薄雾,强化日落纵深感
vec3 finalRGB = mix(mixWarm, duskWarmColor, warmFogFactor);
// 保留原图透明度输出
fragColor = vec4(finalRGB, originColor.a);
}
`;
// 实例化后处理渲染阶段,挂载黄昏着色器与强度变量
window.duskStage = new Cesium.PostProcessStage({
name: 'dusk_warm_effect', // 后处理唯一标识名称
fragmentShader: DuskShader, // 绑定黄昏片元着色器
uniforms: {
duskStrength: duskStrength.value // 初始化强度数值
}
});
// 将黄昏滤镜加入场景后处理管线,立即生效
window.viewer.scene.postProcessStages.add(window.duskStage);
// 标记黄昏状态为开启
isDusk.value = true;
}
};
</script>
3. css样式代码:
css
* {
margin: 0;
padding: 0;
}
.main {
width: 100%;
height: 100vh;
position: relative;
}
.content {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
}
.loading {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 3;
display: flex;
justify-content: center;
align-items: center;
font-size: 34px;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
color: #000000;
}
.btn-border {
position: absolute;
right: 24px;
top: 24px;
z-index: 2;
display: flex;
justify-content: start;
align-items: stretch;
}
.btn {
margin-left: 20px;
cursor: pointer;
}
.slider-border {
width: 260px;
margin-right: 20px;
position: relative;
top: -9px;
}
.slider-label {
font-size: 14px;
}
核心逻辑说明
1. 分层黄昏原理
本文采用 屏幕 Y 轴分层混合:
- 画面上方(天空):暖色雾效更浓。
- 画面下方(地形建筑):保留更多原始细节。
2. 动态强度调节
通过 duskStrength 统一控制:
- 画面压暗程度。
- 暖色调混合比例。
- 远景暖色雾浓度。
拖动滑块即可实时预览不同程度的黄昏氛围。