新闻详情

新闻详情

首页 / 资讯中心 / 详情

Cesium倾斜摄影光照与阴影动态日照配置指南

发布时间:2026/10/2 20:28:53来源:尧图网络
Cesium倾斜摄影光照与阴影动态日照配置指南
上周帮朋友排一个三维场景的问题倾斜摄影模型加载得很顺飞到目标区域一看整片建筑群平得像一张贴纸太阳悬在天上楼体外立面没有明暗过渡地面也找不到一点影子。他第一反应是数据做得不好其实跟他那套倾斜摄影数据一点关系都没有——Cesium 里光照和阴影是两套完全独立的机制各有一套开关任何一个没打开画面都会看起来很平。这类问题我在实际项目里遇到过不止五次几乎每次都卡在同样的几个参数上。这篇内容就把 Cesium 光照效果和倾斜摄影日照阴影这条链路完整拆一遍光照从哪来、阴影由谁控制、倾斜摄影为什么默认不出影子、怎么调质量、怎么控性能、出问题按什么顺序排查。适合已经在用 Cesium 做三维可视化、手上有一份 3DTiles 倾斜摄影数据、想把画面从能看做到能交付的同学也适合准备相关方向面试、想把光照阴影这块讲清楚的读者。代码按 Web 端 Cesium 的常规写法给思路对其它三维引擎也通用。1. 光照和阴影在 Cesium 里是两套独立机制很多人第一次调这块会把开光照和开阴影当成一件事改一个参数期待画面整体变立体结果改了半天没反应。我一开始也这么想后来把渲染管线捋了一遍才明白这俩走的是不同的路径。1.1 scene.globe.enableLighting 只管地球表面viewer.scene.globe.enableLighting这个开关的作用非常明确让地球表面地形、影像根据太阳位置做明暗计算。打开它地球就有了晨昏线朝太阳的一面亮背太阳的一面暗。它默认是false所以很多人加载完地形觉得一片死白就是这个开关没开。viewer.scene.globe.enableLighting true;但要注意它只影响 globe 表面。倾斜摄影是作为Cesium3DTileset挂在scene.primitives上的它不吃 globe 的这套光照参数。所以你打开enableLighting之后地形暗下来了倾斜摄影的楼还是亮的两者对不上看起来特别割裂。这就是我朋友遇到的第一层问题。还有一个容易忽略的点enableLighting打开之后地球会沿晨昏线渐暗远处缩放时还会有一段光照淡出的处理由lightingFadeOutDistance和lightingFadeInDistance控制。默认值很大千万米级如果你做的是城市级局部场景基本碰不到但如果做全球视图这两个值会影响远景的地球是不是全黑。另外scene.globe.dynamicAtmosphereLighting控制大气层是否跟随光照变化默认true。想让大气也跟太阳走再补一句viewer.scene.globe.dynamicAtmosphereLightingFromSun true。1.2 shadowMap 是全局总闸ShadowMode 是逐对象的开关阴影这边是两级控制必须都打开才有影子。第一级是全局的viewer.shadowMap.enabled它是总闸。总闸不开后面怎么设都没用。总闸的另一种开法是在创建Viewer时传shadows: true效果一样。// 写法一构造时开启 const viewer new Cesium.Viewer(cesiumContainer, { shadows: true, terrainShadows: Cesium.ShadowMode.RECEIVE_ONLY }); // 写法二运行时开启 viewer.shadowMap.enabled true;第二级是每个可渲染对象自己的shadows属性取值是Cesium.ShadowMode枚举。这里是最关键的坑Cesium3DTileset.shadows的默认值是ShadowMode.DISABLED。也就是说哪怕你全局开关打开了倾斜摄影依然不投影也不接收阴影因为对象自己被关掉了。对比一下就很清楚Globe.shadows的默认值是RECEIVE_ONLY地形默认接收阴影Model.shadows默认是DISABLED。所以地形上有影子、倾斜摄影没影子这个现象从默认值就能解释。1.3 太阳位置由时钟决定阴影方向跟着太阳走Cesium 的太阳不是固定的它由viewer.clock.currentTime决定位置。这意味着你截屏出来的画面会随当前系统时间变化——白天打开是一个样晚上打开地球那半边就是黑的。// 锁定到一个固定时刻方便出图和对比效果 viewer.clock.currentTime Cesium.JulianDate.fromIso8601(2024-06-21T04:00:00Z); viewer.clock.shouldAnimate false;阴影相机的方向来自scene.light。默认情况下 Cesium 会每帧把太阳方向同步给这个光所以阴影方向跟太阳一致。一旦你手动给scene.light赋了一个自己的DirectionalLight它就跟你走不再自动同步太阳了——这个特性正好可以用来做假太阳后面第 4 节会展开。把这三层关系理清楚后面所有问题都有了定位坐标画面平先看enableLighting有光没影先看shadowMap.enabled地形有影、模型没影直接看tileset.shadows。2. 倾斜摄影没影子九成是这三处没配对把光照和阴影拆开之后倾斜摄影不出影子的原因就收敛到很小的范围了。我按实际排查的频率排个序基本上前两条能解决绝大多数情况。2.1 tileset.shadows 默认 DISABLED必须显式赋值这是最高频的原因没有之一。加载倾斜摄影的标准写法很多人只写 URL 和maximumScreenSpaceError剩下全用默认值阴影自然是不存在的。const tileset viewer.scene.primitives.add( await Cesium.Cesium3DTileset.fromUrl(/data/oblique/tileset.json) ); // 关键显式打开阴影 tileset.shadows Cesium.ShadowMode.ENABLED;这段代码有个细节要注意fromUrl是异步的await回来之后 tileset 才加入场景。shadows属性在 tileset 加入primitives之后随时可以改不需要等到readyPromiseresolve但为了逻辑清晰我一般还是等加载完成后再统一配参数。还有一点如果你的倾斜摄影是分区块加载的比如一个城市切成几十个 tileset.json每一个 tileset 都要单独设置shadows。这个坑我踩过一次某项目只给主城区那个 tileset 开了阴影边缘几个区块全是平的排查了半天才发现是漏了几个对象。2.2 ShadowMode 四种取值别一律用 ENABLEDCesium.ShadowMode一共四个值用错了不是没效果而是效果不符预期或者白白浪费性能。取值含义典型用法DISABLED不投影也不接收远处小物件、装饰性模型默认值ENABLED既投影又接收主体建筑、需要互相遮挡的模型群CAST_ONLY只投影不接收投到地面/其它模型上的物体比如高架桥、塔吊RECEIVE_ONLY只接收不投影地面、道路这类被投影的承载面实际项目里最省性能的组合是主体倾斜摄影用ENABLED地形用RECEIVE_ONLY默认就是次要模型用DISABLED。我曾经图省事把所有 tileset 全设成ENABLED结果帧率掉了将近一半因为每个对象都要参与两遍渲染——一遍生成阴影贴图一遍做接收计算。还有一个反直觉的点如果一个模型只需要接收阴影、完全不需要投影比如背景里的一片远景建筑设成RECEIVE_ONLY比ENABLED省资源因为RECEIVE_ONLY的对象不会被渲染进阴影贴图。2.3 模型自身的问题法线、单面片、透明贴图如果tileset.shadows确实设成了ENABLED但阴影还是长得不对那问题往往在模型本身。倾斜摄影是实景建模的产物有几类几何特征会直接影响阴影效果。第一类是法线异常。倾斜摄影数据在生产过程中会做网格简化和重拓扑偶尔出现法线翻转的面片。这类面片在光照计算里会显得特别黑或者特别亮阴影投射时也可能出现撕裂。判断方法是把tileset.shadows临时设成DISABLED如果关掉阴影后画面依然有明显异常暗块那基本就是法线问题需要在建模端修。第二类是单面片几何。实景建模里常见的围墙、广告牌、幕布这类薄片结构因为只有一个面、没有厚度投影时容易出现阴影缺失或者只投出一半。这种情况可以尝试关掉背面剔除tileset.backFaceCulling false;代价是渲染量会增加只在确实需要的时候开。第三类是透明贴图。用 alpha 通道做出来的树丛、栏杆投影时会变成一整块实心方形阴影因为阴影贴图默认不参与 alpha 测试。这个目前没有特别优雅的通用解法我的处理方式是把这类小物件单独拆成一个 tileset设成RECEIVE_ONLY或者直接DISABLED牺牲它们的投影换取整体观感干净。3. 阴影质量的调法从锯齿、漏光到阴影错位阴影能出来之后下一步就是好不好看。这一步全是参数活调对了画面立刻上一个档次调错了就是一堆莫名其妙的瑕疵。3.1 size 和 maximumDistance 决定清晰度与开销阴影贴图本质上是一张深度图分辨率越高边缘越锐利。viewer.shadowMap.size 2048; // 默认 2048可调 1024 / 4096 viewer.shadowMap.maximumDistance 5000; // 默认 5000 米超过这个距离不计算阴影size从 2048 提到 4096阴影边缘会明显干净但显存和绘制开销是平方级增长4096 贴图在移动端基本别想。我一般在桌面端固定 2048只有在做单体的重点展示比如一个小区、一栋楼时才上 4096。maximumDistance更值得关注。默认 5000 米意味着离摄像机超过 5 公里的对象不参与阴影计算。做城市级大场景时这个默认值其实挺合理但如果你的场景只有几百米范围把maximumDistance调到 1000~2000会有两个好处一是阴影贴图的精度全部分配给近处边缘更锐二是远处的对象直接跳过阴影计算省性能。这里有个经验值maximumDistance设成场景主体范围的两倍左右比较合适。太小了边缘建筑会突然脱影太大了贴图精度被摊薄阴影边缘全是锯齿。3.2 normalOffset 和阴影漏光、阴影痤疮阴影贴图有个经典问题叫shadow acne阴影痤疮表现是平面上出现一条条明暗相间的条纹尤其在斜射光下特别明显。成因是深度比较时浮点精度不够面片自己跟自己比出了深度差。Cesium 给的解法是normalOffset默认就是trueviewer.shadowMap.normalOffset true; // 默认开启沿法线方向偏移采样点如果你把倾斜摄影的法线问题修完了还是有条纹可以检查一下这个值是不是被谁改成了false。反过来如果偏移量在特定几何上过大会出现另一个问题物体和它的阴影脱开一点缝看起来像悬浮。这个缝隙叫 peter-panning通常出现在薄物体上。Cesium 暴露出来的可调项有限遇到这种情况我的做法是调整maximumDistance让阴影精度集中在近处缝隙会明显减小。3.3 softShadows 和 darkness 是观感开关这两个参数不改变阴影的正确性只改变观感但对成图效果影响很大。viewer.shadowMap.softShadows true; // 阴影边缘软化 viewer.shadowMap.darkness 0.3; // 阴影浓度0 全黑1 无阴影softShadows打开后阴影边缘从硬边变成渐变接近真实日照下那种柔和过渡。代价是有一定性能开销而且边缘会略微糊。做建筑漫游、日照分析这类需要真实感的场景建议开做需要精准边界的分析类场景比如阴影范围测算反而应该关掉保持硬边便于判读。darkness的默认值是 0.3表现是比较浅的灰影。做正午强光效果时可以调到 0.5~0.6阴影更实做清晨薄雾效果时可以降到 0.15 左右。我有个习惯在时间轴上做动态日照时会按太阳高度角联动调整darkness——太阳越低阴影越淡。这属于物理上不完全正确但观感很好的小技巧实际项目里挺吃香。4. 动态日照让太阳走起来阴影跟着走静态日照截图只是起点很多项目真正要的是太阳随时间移动、阴影实时跟着变。这块 Cesium 支持得很好但有几个隐藏的坑。4.1 时钟的三个参数决定一切太阳位置由时钟驱动涉及三个参数当前时间、时间流速、是否播放。viewer.clock.currentTime Cesium.JulianDate.fromIso8601(2024-06-21T02:00:00Z); viewer.clock.multiplier 3600; // 1 秒真实时间 1 小时场景时间 viewer.clock.shouldAnimate true;multiplier建议设成 1800 到 3600也就是半秒到一秒走一小时。设得太大比如 86400太阳是转得快了但阴影贴图每帧都在重算帧率会掉得很明显。我实测过一个城市级倾斜摄影场景multiplier从 3600 提到 21600帧率从 45 左右掉到 25 上下。时间范围建议落在白天也就是当地时间的 6 点到 18 点之间。用一个ClockRange.LOOP_START让它在设定的区间里循环比让它一直往前跑更好控viewer.clock.startTime Cesium.JulianDate.fromIso8601(2024-06-21T00:00:00Z); viewer.clock.stopTime Cesium.JulianDate.fromIso8601(2024-06-21T12:00:00Z); viewer.clock.clockRange Cesium.ClockRange.LOOP_START;4.2 用自定义 DirectionalLight 做假太阳有一个需求很常见我不想按真实太阳轨迹走就想让阴影固定朝某个方向打或者做一种艺术化的打光。这时候直接替换scene.lightviewer.scene.light new Cesium.DirectionalLight({ direction: new Cesium.Cartesian3(-0.5, -0.5, -0.7) });这个操作会让阴影方向完全由你控制跟时间轴脱钩。做建筑日照分析演示的时候特别有用——你想展示同一个建筑在四个朝向上的阴影变化直接替换 direction 就行不用去算具体几点钟太阳在哪。注意一旦替换了scene.lightCesium 就不会再自动把太阳方向同步给它所以如果你后续又想让太阳接管需要手动重新赋值或者重新构造。这不是 bug是设计如此。还有个常见的相机头灯写法让光始终跟着相机方向保证任何角度看过去都有明暗层次viewer.scene.light new Cesium.DirectionalLight({ direction: viewer.scene.camera.directionWC });代价是阴影方向会跟着你转视角乱跑只在纯展示场景用不要用在日照分析里。4.3 光照淡出与夜间过渡做全球或者大范围场景时会碰到一个问题摄像机拉远之后远处的地球开始出现明暗分界但倾斜摄影那一块区域还亮着视觉上很跳。这是lightingFadeOutDistance系列参数的地盘viewer.scene.globe.lightingFadeOutDistance 10000000; viewer.scene.globe.lightingFadeInDistance 20000000;城市级场景几十公里范围基本不用动这两个值动了反而可能出现奇怪的半亮过渡带。我的建议是场景范围小于 100 公里这两个参数保持默认别碰。另外如果时间轴走到了夜间整个场景会变暗倾斜摄影上的自发光材质如果有会显得特别突兀。这时候可以配合scene.highDynamicRange做一下色调映射或者干脆在时钟上限制范围不让它走到夜间隔壁去。5. 大范围倾斜摄影的性能账我实测的几个档位阴影是好东西但它真的费性能。倾斜摄影动辄几百万面片再加上阴影贴图那两遍渲染帧率掉下去是必然的。这一节给一些量化参考都是在同一台开发机、同一份城市级倾斜摄影数据上测的只能当量级参考具体还得看你的数据。5.1 阴影参数与帧率对照测试条件倾斜摄影约 8GB 数据量视点高度 300 米视野内可见约 200 栋建筑。配置关键参数平均帧率观感评价不开阴影shadowMap.enabled false60画面很平无立体感基础阴影size1024, maximumDistance200052有影边缘有锯齿推荐配置size2048, maximumDistance2000, softShadowstrue42边缘柔和观感好高质量size4096, maximumDistance5000, softShadowstrue24边缘锐利明显卡顿结论很清楚size是性能杀手从 2048 到 4096 帧率几乎腰斩。softShadows的代价大概在 10% 左右性价比很高建议一直开着。maximumDistance从 5000 降到 2000帧率回升明显而且因为精度集中阴影边缘反而更好看。这个参数是我最推荐优先调的一个。5.2 分级阴影近处精细远处关闭只靠单个参数调总会在近处不够好和远处太浪费之间妥协。真正有效的做法是按对象分档配置。具体思路是把倾斜摄影按区域拆成多个 tileset近景区域用ENABLED 高分辨率远景区域用RECEIVE_ONLY。// 近景区块完整阴影 nearTileset.shadows Cesium.ShadowMode.ENABLED; // 远景区块只接收不投影 farTileset.shadows Cesium.ShadowMode.RECEIVE_ONLY;这么做还有个额外好处远景区块不参与阴影贴图渲染阴影贴图里的有效像素全给了近处等于变相提升了分辨率。如果你的数据不支持按区域拆分还有个替代方案——监听相机高度动态切换参数viewer.scene.preRender.addEventListener(() { const h viewer.camera.positionCartographic.height; if (h 3000) { viewer.shadowMap.maximumDistance 1000; } else if (h 1000) { viewer.shadowMap.maximumDistance 3000; } else { viewer.shadowMap.maximumDistance 5000; } });注意preRender里不要做重计算赋值这种轻操作没问题但如果里面写了几何运算或者遍历会直接拖垮帧率。我见过有人在preRender里遍历所有 tileset 改样式帧率掉到个位数。5.3 其它几个容易被忽略的省性能开关除了阴影参数本身还有几个开关会间接影响阴影场景的帧率。tileset.maximumScreenSpaceError控制瓦片加载精度。开着阴影的时候我一般会提到 20 左右比默认的 16 稍微降一点精度换来的是更少的瓦片参与阴影渲染。画面损失很小帧率收益挺明显。tileset.skipLevelOfDetail打开后会跳过低精度层级直接加载高清瓦片在有阴影的场景里可能造成阴影突然出现的跳变建议关掉。还有scene.globe.enableLighting打开后地形明暗计算也是开销。如果你的场景是纯局部、地形占比很小其实可以考虑关掉它、只用阴影画面观感差异不大。这个属于按需取舍不是通用建议。6. 阴影不出来时按这个顺序往下查前面讲的是怎么配但实际项目里更多时候是配了没效果。我把这套排查顺序固化成了一份清单基本上从上往下一路看下来问题都能定位到。6.1 六步排查链路第一步看时钟。把viewer.clock.currentTime打出来确认落在白天。如果当前系统时间是晚上太阳在地球背面整个场景就是黑的怎么看都不像有阴影。这一步最容易排查也最容易被忽略我至少遇到三次是其实只是天黑了。第二步看全局开关。viewer.shadowMap.enabled是不是trueviewer.shadowMap.isSupported是不是true。后者在某些设备上会是false说明当前 WebGL 环境不支持深度纹理阴影功能直接不可用。这种情况要检查浏览器版本和显卡驱动。第三步看光照开关。如果画面是一片死白先开viewer.scene.globe.enableLighting true。没有光照计算的时候即使有阴影贴图观感上也很难看出立体感。第四步看对象开关。tileset.shadows是不是ENABLED。这里有个检查技巧把值改成RECEIVE_ONLY如果画面出现变化比如模型不再投影但开始接收说明属性生效了问题在别处如果完全没变化说明 tileset 可能压根没加载成功。第五步看距离。视点高度如果超过shadowMap.maximumDistance阴影会被整体裁掉。把摄像机拉近到 1 公里以内再看一眼如果阴影出现了那就是距离参数的问题。第六步看模型。前面几步都对还是没影那就是模型几何本身的问题——法线异常、单面片、或者数据本身就没有法线信息。这时候需要拿一个简单的Model或者Box放在同一位置做对照如果方块有影、倾斜摄影没影可以确定是数据侧的问题。6.2 常见现象与原因对照现象最可能的原因处理方式地形有影倾斜摄影没有tileset.shadows 为默认 DISABLED设为 ENABLED完全没有影地形也平shadowMap.enabled 未开构造时传 shadows: true画面一片死白没有明暗globe.enableLighting 未开设为 true阴影边缘全是锯齿size 太小或 maximumDistance 太大size 提至 2048距离收窄平面出现明暗条纹normalOffset 被关或法线异常开 normalOffset检查数据晚上打开没有影时钟落在夜间锁定 currentTime 到白天部分区块没影该区块 tileset 漏设 shadows遍历所有 tileset 统一设置树丛的影是实心方块alpha 贴图不参与阴影测试该类对象设 RECEIVE_ONLY这张表我基本是贴在项目文档里的接手新项目先对一遍能省掉大量猜测时间。顺带说一句这几点也是这个方向面试里经常被问到的内容——面试官问倾斜摄影加载后没有阴影是怎么回事答出tileset.shadows 默认 DISABLED、需要配合 shadowMap 全局总闸、并且地形和模型分属两套开关基本就答到点子上了。提示调试阶段建议把viewer.shadowMap.size临时调到 1024改参数后看变化更快确定方向之后再往上调。分辨率高的时候每次改参数都要等渲染稳定反而影响排查效率。最后分享两个我自己在项目里固定下来的习惯。第一个是把光照阴影配置封装成一个函数接收 viewer 和参数对象内部统一处理时钟、全局开关、光照开关和所有 tileset 的 shadows 属性。这样迁移到新项目只要调一次不会出现某个区块漏设这种事。第二个是准备两组预设参数一组是编辑态关阴影、低精度、帧率优先一组是出图态开阴影、高分辨率、softShadows、固定时间做方案讨论的时候切编辑态汇报截图的时候切出图态效率差挺多的。至于后续的扩展这套光照阴影的框架还能往日照分析上走——按小时粒度采样太阳位置算出某个立面的累计受照时长做成热力色带叠在建筑上。这个我做过一个小版本核心就是替换scene.light然后循环采样配合倾斜摄影单体化之后按构件统计思路和这篇文章讲的东西是一脉相承的。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Marketing Skills 社交平台限制速查:Hashtag、字符数与可见文本阈值全平台指南 2026/10/2 21:23:02

Marketing Skills 社交平台限制速查:Hashtag、字符数与可见文本阈值全平台指南

AI 技能人工智能 【免费下载链接】marketingskills Marketing skills for Claude Code and AI agents. CRO, copywriting, SEO, analytics, and growth engineering. 项目地址: https://gitcode.com/GitHub_Trending/mar/marketingskills 点击查看 免费下载 导读 …

阅读更多 →
从激光雷达到LOFIC纯视觉:智能驾驶传感器架构演进与工程实践 2026/10/2 21:22:49

从激光雷达到LOFIC纯视觉:智能驾驶传感器架构演进与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
硬件消抖电路设计原理与STM32实战指南 2026/10/2 21:22:48

硬件消抖电路设计原理与STM32实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
C#上位机Modbus RTU主站实战:报文解析、CRC16与串口联调 2026/10/2 21:22:40

C#上位机Modbus RTU主站实战:报文解析、CRC16与串口联调

简介:C# Modbus RTU通信库及配套示例工程,面向工业自动化与上位机开发场景,帮助开发者在.NET环境中通过串口与PLC、传感器等Modbus从站设备高效交换数据。资源内含可直接调用的通信库源码,覆盖串口参数配置、寄存器与线圈读写、常…

阅读更多 →
ABAP视图维护:创建、增强与过滤的实战指南 2026/10/2 21:22:33

ABAP视图维护:创建、增强与过滤的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
在 gitoxide 仓库中使用 Tix 编辑提交历史与修复 CI:Agent 级操作全指南 2026/10/2 21:22:33

在 gitoxide 仓库中使用 Tix 编辑提交历史与修复 CI:Agent 级操作全指南

版本控制CLI 【免费下载链接】gitoxide An idiomatic, lean, fast & safe pure Rust implementation of Git 项目地址: https://gitcode.com/GitHub_Trending/gi/gitoxide 点击查看 免费下载 本文以 gitoxide 仓库中面向 Agent 的 Tix 技能文档 为核心&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉