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>
相关推荐
数据知道3 天前
视觉伪装(下):WebGL 渲染器与厂商特征的底层伪造与屏蔽
javascript·数据采集·webgl·指纹浏览器
niconicoC3 天前
让 Three.js 场景更真实:我用高斯泼溅和 SparkJS 做了一个可交互的 3D Demo
前端·webgl
sinat_384503114 天前
【无标题】
unity·webgl
山河木马9 天前
无框架-原生webGL渲染-底层入门-1
前端·javascript·webgl
拾忆丶夜10 天前
unity webgl 阴影条纹问题
unity·游戏引擎·webgl
GISer_Jing14 天前
Three.js着色器编译机制深度解析
javascript·webgl·着色器
GISer_Jing16 天前
WebGL|Three.js渲染管线核心技术解析
java·javascript·webgl
丷丩16 天前
MapLibre GL JS第12课:检查WebGL支持
前端·javascript·map·webgl·mapbox·maplibre gl js
平行云17 天前
实时云渲染预启动技术解析:UE数字孪生应用的延迟优化机制(二)
linux·unity·ue5·webgl·实时云渲染·云桌面·像素流
平行云17 天前
实时云渲染预启动技术解析:UE数字孪生应用的延迟优化机制(一)
linux·ue5·webgl·数字孪生·云渲染·实时云渲染·像素流