SuperMap iClient3D for WebGL 如何实现动态日照阴影效果

在三维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>
相关推荐
qq_283720053 小时前
WebGL基础教程(十四):投影矩阵深度解析——正交 vs 透视,彻底搞懂3D视觉魔法
3d·矩阵·webgl
Jack Yan4 小时前
WebGL平台动态修改窗口大小
webgl
小彭努力中17 小时前
192.Vue3 + OpenLayers 实战:点击地图 Feature,列表自动滚动定位
vue·webgl·openlayers·geojson·webgis
平行云1 天前
数字孪生信创云渲染系列(一):混合信创与全国产化架构
unity·ue5·3dsmax·webgl·gpu算力·实时云渲染·像素流送
sin°θ_陈1 天前
CVPR 2026的3DGS卷到什么地步?工程语义上探:BrepGaussian如何打通图像到CAD的最后一公里?(Part III 1-3)
python·深度学习·算法·机器学习·3d·webgl
花姐夫Jun2 天前
WebGL学习-czm_getMaterial详解
学习·webgl
花姐夫Jun4 天前
WebGL学习-夹角的归一化
学习·webgl
一拳不是超人5 天前
Three.js一起学-如何通过官方例子高效学习 Three.js?手把手带你“抄”出一个3D动画
前端·webgl·three.js
qq_283720055 天前
WebGL基础教程(十四):网络图片纹理映射渲染完整实战(新手也能轻松上手)
网络·webgl