SuperMap iClient3D for WebGL 如何实现动态日照阴影效果
作者:ZLF
在三维GIS可视化中,动态日照阴影能够极大提升场景的真实感和沉浸感。如何模拟太阳轨迹变化,实现随时间流动的光影效果,让场景更具表现力?让我们一起看看吧!
一、数据制作
对于上述视频中的三维缓存数据制作,此处不做讲述,如有需要可参考 iDesktopX 帮助文档:https://help.supermap.com/iDesktopX/zh/
二、实现思路
基本实现思路如下图所示:
三、示例完整代码
<!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>