在三维GIS可视化中,动态日照阴影能够极大提升场景的真实感和沉浸感。如何模拟太阳轨迹变化,实现随时间流动的光影效果,让场景更具表现力?让我们一起看看吧!

一、数据制作
对于上述视频中的三维缓存数据制作,此处不做讲述,如有需要可参考 iDesktopX 帮助文档:https://help.supermap.com/iDesktopX/zh/
二、实现思路
基本实现思路如下图所示:

三、示例完整代码
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>日照动画</title>
<link href="../../Build/SuperMap3D/Widgets/widgets.css" rel="stylesheet">
<link href="./css/bootstrap.min.css" rel="stylesheet">
<link href="./css/pretty.css" rel="stylesheet">
<link href="./style/shadowQuery.css" rel="stylesheet">
<script src="./js/jquery.min.js"></script>
<script src="./js/bootstrap.min.js"></script>
<script src="./js/tooltip.js"></script>
<script src="./js/config.js"></script>
<script type="text/javascript" src="../../Build/SuperMap3D/SuperMap3D.js"></script>
<style>
#animationControls {
margin-top: 10px;
padding: 10px;
background: rgba(255,255,255,0.8);
border-radius: 5px;
}
#currentTimeDisplay {
font-weight: bold;
color: #333;
margin: 5px 0;
}
.speed-control {
width: 100%;
margin: 5px 0;
}
</style>
</head>
<body>
<div id="Container"></div>
<div id='loadingbar' class="spinner">
<div class="spinner-container container1">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container2">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container3">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
</div>
<div id='toolbar' class="param-container tool-bar">
<div class="param-item">
<b>日期选择:</b>
<input id="selDate" type="date" value="2026-03-20"/>
</div>
<div class="param-item">
<b>开始时间:</b>
<select id="startTime" class="form-control">
<option value="6">6:00</option>
<option value="7">7:00</option>
<option value="8">8:00</option>
<option value="9">9:00</option>
<option value="10" selected>10:00</option>
<option value="11">11:00</option>
<option value="12">12:00</option>
</select>
</div>
<div class="param-item">
<b>结束时间:</b>
<select id="endTime" class="form-control">
<option value="12">12:00</option>
<option value="13">13:00</option>
<option value="14">14:00</option>
<option value="15">15:00</option>
<option value="16">16:00</option>
<option value="17">17:00</option>
<option value="18" selected>18:00</option>
</select>
</div>
<div class="param-item">
<button type="button" id="sunlight" class="button black">日照阴影动画</button>
<button type="button" id="clear" class="button black">清除</button>
</div>
<div id="animationControls" style="display:none;">
<div id="currentTimeDisplay">当前时间: 10:00:00</div>
<button type="button" id="playAnimation" class="button black">播放</button>
<button type="button" id="pauseAnimation" class="button black">暂停</button>
<button type="button" id="stopAnimation" class="button black">停止</button>
<div>
<label>播放速度:</label>
<input type="range" id="speedControl" class="speed-control" min="1" max="100" value="50">
<span id="speedValue">50x</span>
</div>
</div>
</div>
<script type="text/javascript">
// 页面加载完成后执行,SuperMap3D 为传入的全局对象
function onload(SuperMap3D) {
// 获取引擎类型(可能用于 WebGL 上下文配置)
var EngineType = getEngineType();
// 创建三维视图器,配置阴影、时间轴、动画等
var viewer = new SuperMap3D.Viewer('Container', {
shadows: true, // 开启阴影
shouldAnimate: true, // 允许动画
contextOptions: {
contextType: Number(EngineType), // 设置 WebGL 上下文类型
}
});
// 等待场景加载完成后执行初始化
viewer.scenePromise.then(function(scene){
init(SuperMap3D, scene, viewer);
});
}
// 初始化函数:设置场景、加载图层、绑定事件
function init(SuperMap3D, scene, viewer) {
// 设置分辨率适应设备像素比
viewer.resolutionScale = window.devicePixelRatio;
var scene = viewer.scene;
// 设置阴影暗度
scene.shadowMap.darkness = 0.4;
// 关闭 HDR
scene.hdrEnabled = false;
// 显示太阳
scene.sun.show = true;
// 开启地球光照
scene.globe.enableLighting = true;
// 显示工具栏
$('#toolbar').show();
// 移除加载动画
$('#loadingbar').remove();
// 如果不支持深度纹理拾取,弹出警告
if(!scene.pickPositionSupported){
alert('不支持深度纹理,阴影分析功能无法使用!');
}
// 获取 Widget 对象,用于错误处理等
var widget = viewer.Widget;
try{
// 打开三维场景(SCP 服务),返回一个 Promise 数组
var buildPromise = scene.open("http://localhost:8090/iserver/services/3D-building/rest/realspace");
// 等待所有图层加载完成
SuperMap3D.when.all(buildPromise,function(layers){
// 遍历图层,设置不可选中,并开启阴影
for(var i = 0; i < layers.length; i++){
layers[i].selectEnabled = false;
layers[i].shadowType = 2;
}
setCurrentTime();
function setCurrentTime() {
viewer.clock.multiplier = 1; // 重置速度倍率
viewer.clock.shouldAnimate = false; // 停止动画
}
// 更新当前时间显示的函数
function updateCurrentTimeDisplay() {
var currentTime = SuperMap3D.JulianDate.toDate(viewer.clock.currentTime);
var timeString = currentTime.toLocaleTimeString(); // 格式化为本地时间字符串
$('#currentTimeDisplay').text('当前时间: ' + timeString);
}
// 监听时钟 tick 事件,每次更新显示当前时间
viewer.clock.onTick.addEventListener(function() {
updateCurrentTimeDisplay();
});
// 点击"日照阴影动画"按钮:设置动画开始、结束时间,并启动动画
$('#sunlight').click(function(){
var dateVal = $("#selDate").val();
var startTime = new Date(dateVal);
var endTime = new Date(dateVal);
var shour = Number($("#startTime :selected").val()); // 开始小时
var ehour = Number($("#endTime :selected").val()); // 结束小时
// 检查结束时间是否大于开始时间
if(shour >= ehour) {
alert("结束时间必须大于开始时间");
return;
}
// 设置开始和结束的完整日期时间
startTime.setHours(shour);
endTime.setHours(ehour);
// 配置时钟范围
viewer.clock.startTime = SuperMap3D.JulianDate.fromDate(startTime);
viewer.clock.stopTime = SuperMap3D.JulianDate.fromDate(endTime);
viewer.clock.currentTime = viewer.clock.startTime.clone(); // 当前时间设为开始时间
// 设置时钟范围为:播放完成后,重新从时间轴起点开始循环播放
viewer.clock.clockRange = SuperMap3D.ClockRange.LOOP_STOP;
// 设置播放速度倍率
viewer.clock.multiplier = 3600;
// 开始动画
viewer.clock.shouldAnimate = true;
// 显示动画控制面板
$('#animationControls').show();
});
// 播放按钮:继续动画
$('#playAnimation').click(function() {
viewer.clock.shouldAnimate = true;
});
// 暂停按钮:暂停动画
$('#pauseAnimation').click(function() {
viewer.clock.shouldAnimate = false;
});
// 停止按钮:暂停并重置到开始时间
$('#stopAnimation').click(function() {
viewer.clock.shouldAnimate = false;
viewer.clock.currentTime = viewer.clock.startTime.clone();
updateCurrentTimeDisplay(); // 更新显示
});
// 速度控制滑动条
$('#speedControl').on('input', function() {
var speed = parseInt($(this).val());
var actualSpeed = 60 + (speed * 360); // 自定义速度计算公式
viewer.clock.multiplier = actualSpeed;
$('#speedValue').text(speed + 'x');
});
// 清除按钮:隐藏动画控制面板并停止动画
$('#clear').click(function(){
$('#animationControls').hide();
viewer.clock.shouldAnimate = false;
});
// 当开始时间、结束时间或日期改变时,重新设置当前时间(但不启动动画)
$('#startTime, #endTime, #selDate').change(function(){
setCurrentTime();
});
}, function(e){
// 图层加载失败的处理:如果允许显示渲染错误,则显示错误面板
if (widget._showRenderLoopErrors) {
var title = '加载SCP失败,请检查网络连接状态或者url地址是否正确?';
widget.showErrorPanel(title, undefined, e);
}
});
}
catch(e){
// 捕获渲染或其他异常,显示错误面板
if (widget._showRenderLoopErrors) {
var title = '渲染时发生错误,已停止渲染。';
widget.showErrorPanel(title, undefined, e);
}
}
}
// 如果 SuperMap3D 对象已定义,则直接调用 onload
if (typeof SuperMap3D !== 'undefined') {
window.startupCalled = true;
onload(SuperMap3D);
}
</script>
</body>
</html>