年初接了一个三维规划审查项目,技术栈是UE5加SuperMap Hi-Fi 3D SDK for Unreal,负责把城市级的三维GIS场景接进来,做量测、剖切和业务对象选中。功能做到一半,客户提了个看起来很小的需求:默认的模型选中高亮颜色不够醒目,想把选中建筑改成橙色,选中管线改成荧光绿,最好还能按业务类型区分,方便评审汇报时一眼看清对象归属。
我一开始以为这不就是个颜色属性,改个RGB值就行。结果真正动手才发现,SuperMap Hi-Fi 3D SDK for Unreal这套东西虽然跑在Unreal里,但底层渲染路径和原生的Editor Selection完全不是一回事。直接改EditorPreferences里的选中色根本不生效,因为模型根本不是你摆在地图里的静态Mesh,而是SDK动态生成的场景内容和分块网格体。断断续续折腾了两天,才把整条链路摸清楚。这篇就把我实际验证过的改法、排查思路和踩过的坑完整写出来,给后面用同一套SDK做业务选中的朋友当个参考。
1. 先从源头看清:SuperMap Hi-Fi 3D SDK的高亮到底是怎么画出来的
1.1 我遇到的场景:默认高亮颜色为什么不够用
最初跑通示例工程的时候,选中一个建筑,默认高亮是偏青蓝的半透明色。这个颜色在白天阳光底图上还算清楚,可一旦切到夜景底图或者深色卫星影像背景,辨识度就急剧下降。客户原话是"蓝不蓝绿不绿的,堆在一起根本分不清哪个是选中的楼"。
需求本身不复杂,但给了三个条件:第一,颜色要按对象类型区分,比如建筑、管线、道路分别用不同色;第二,选中状态要能叠加呼吸效果;第三,不能影响未选中对象的原始显示效果。这就意味着不能简单用一个全局颜色覆盖所有,必须在上层做对象分类,再逐个下发高亮色。
搞清楚这个前提后,我开始倒查SDK到底是怎么实现"选中高亮"的,因为只有知道颜色从哪里来,才知道该去哪里改。
1.2 SDK选中高亮的底层渲染路径
在传统Unreal开发里,想让一个Actor高亮,通常有三条路:
- 修改
Actor->SetActorEnableCollision配合原生高亮; - 通过
CustomDepthStencil给模型描边; - 动态替换模型材质或调节Emissive自发光参数。
但对于SuperMap Hi-Fi 3D SDK for Unreal加载出来的场景,这三条路都不完全适用。SDK在运行时拿到的是iServer发布的三维服务数据,场景里的建筑、道路、管线不是一个个关卡里摆好的Actor,而是通过地形分块、LOD动态加载生成的Mesh。你和某个模型交互,本质上是SDK在维护一个拾取/选中管理模块,它会根据命中到的模型ID去找对应的Mesh分区,然后给这个分区里的SubMesh换上一套高亮用的材质,或者叠加一个半透明叠加Pass。
我验证了一下,SDK的高亮整体上是"材质替换+透明度混合"的思路,而不是纯后处理描边。证据有两个:一是修改高亮材质的颜色参数,能直接影响高亮显示的最终颜色;二是选中对象之后,用SceneCapture截图,选中区域的Mesh颜色是整体被混合覆盖的,并不是只有边缘轮廓线。
如果把这一层关系理清楚,就能明白一个关键结论:你要改的颜色,实际上是SDK内部那张高亮材质(或者材质实例)的参数,而不是Unreal编辑器里的SelectionColor,也不是引擎默认的SelectionOutlineColor。这也是很多人改了半天空不掉颜色的根本原因。
1.3 高亮颜色的来源与优先级
结合SDK的公开接口和实际行为,我把高亮颜色的来源分成四个层级,优先级从高到低排:
| 优先级 | 颜色来源 | 作用范围 | 灵活度 |
|---|---|---|---|
| 1 | 运行时按对象/图层调用的高亮接口 | 单独对象或图层 | 最高,可任意控制 |
| 2 | 高亮材质实例中的颜色参数 | 全局,所有选中对象共用 | 中,可做复杂材质效果 |
| 3 | SDK初始化配置文件中的默认高亮参数 | 全局,插件加载时生效 | 低,影响所有场景 |
| 4 | SDK内置默认高亮材质 | 兜底 | 最低 |
记住这个优先级很重要。很多项目出现"改了配置没生效"的情况,十有八九是因为SDK某处代码已经以更高优先级设置了颜色,配置值根本没机会展示出来。我后来在项目里定的策略是:整体色系用材质实例控制,业务选中颜色用运行时接口单独下发,配置文件只做兜底。这样无论SDK怎么更新版本,颜色表现都能保持稳定。
2. 修改高亮颜色的三条实操路径
2.1 路径一:通过SDK运行时接口直接设置颜色
这是最推荐的一条路,适合需要根据业务动态切换颜色的场景。以我用的SuperMap Hi-Fi 3D SDK for Unreal 11i版本为例,在场景初始化完成后,拿到场景Actor中的选中管理模块,调用类似下面这样的接口:
// 获取场景加载时生成的Actor ASuperMapSceneActor* SceneActor = ...; // 方式1:设置所有选中对象的统一高亮颜色 FLinearColor HighlightColor = FLinearColor(0.0f, 0.5f, 1.0f, 1.0f); SceneActor->GetSelectionManager()->SetHighlightColor(HighlightColor); // 方式2:按图层分别设置(11i版本多数支持) SceneActor->GetSelectionManager()->SetLayerHighlightColor(TEXT("建筑"), FColor::FromHex(TEXT("#FF8C00"))); SceneActor->GetSelectionManager()->SetLayerHighlightColor(TEXT("管线"), FLinearColor(0.0f, 1.0f, 0.2f));注意,不同小版本之间方法命名可能有差异。我见过老一点的SDK用的是SetSelectionColor,新版本改成了SetHighlightColor,还有的版本需要先拿到USelectionLayer再调用。所以拿到SDK之后不要凭记忆敲,先打开Plugins/SuperMap/Source目录下的头文件,搜索Highlight或Selection关键字,确认当前版本真实暴露了哪些接口。
接口调用完了,如果颜色没变,先检查两件事:一是是否在SDK完成场景加载后调用,二是选中对象的那一层是否已经设置了独立颜色。单独的SetHighlightColor优先级低于按图层的SetLayerHighlightColor,这点和1.3里列的优先级是对应的。
2.2 路径二:通过替换高亮材质实例来做深度定制
如果你希望高亮不只是一个"变个颜色"这么简单,而是想要描边、边线变色、呼吸闪烁、透明度渐变等复合效果,那就得直接动材质。
SDK的插件目录下面,材质命名通常是M_SuperMap_Highlight或MI_SuperMap_HighlightColor,可以在Unreal的Content Browser里搜索"SuperMap"关键词找到它。千万不要直接改这个原始材质,否则SDK一旦重新加载或覆盖文件,你的改动就没了。正确做法是创建一个材质实例:
在Content Browser中右键 -> Create Material Instance 父材质选择 M_SuperMap_Highlight 生成的实例命名为 MI_Highlight_Orange然后在材质实例的细节面板里,把HighlightColor(具体参数名以材质实际为准)改成你要的橙色,HighlightIntensity控制强度,Opacity控制透明度。保存之后,如果你要走"全局统一风格"这条路,可以在SDK的初始化配置里把高亮材质替换成这个实例;如果只想对部分对象生效,甚至可以运行时动态设置材质实例参数。
这段实操下来,我觉得材质实例这条路是最可控的,因为它本质上是利用Unreal原生的材质系统在做事,所有材质相关的调试工具、性能分析和后期处理都能用上。缺点也很明显:它对不熟悉材质编辑器的朋友有门槛,而且一旦SDK内部在特定帧率下频繁重置材质参数,你设置的值可能被覆盖,需要在SDK注册的回调事件里面重新设置。
2.3 路径三:通过初始化配置/配置文件预设高亮颜色
有的项目不打算写代码,只想在SDK初始化阶段把默认高亮色调好,不想每次运行都手动调接口。这种情况可以找SDK插件目录下的配置文件,通常是这样的路径:
[ProjectRoot]/Plugins/SuperMap/Config/SuperMapSDK.ini或者是带XML格式的全局配置:
<Scene> <Highlight> <Color R="255" G="140" B="0" A="180" /> </Highlight> </Scene>具体是ini还是xml,看版本。改完之后重启Unreal编辑器或者重新打包项目才会生效。这里有个容易踩的坑:如果你用的是从SuperMap iServer动态加载的三维服务,服务端图层的状态有时会覆盖客户端配置。比如数据在iServer发布时带有自己的默认样式,SDK加载后优先认服务端样式,你的配置文件颜色反而成了"没有生效"的那个。碰到这种情况,要么把服务端图层样式一并改掉,要么在客户端初始化完成后主动调用一遍运行时接口,用路径一的方式强制覆盖。
2.4 三条路径怎么选
拿我自己的项目举例子:
- 如果你的目标只是"把默认高亮色从青色改成橙色",且没有复杂的业务逻辑,直接用路径三改配置文件最快。
- 如果高亮颜色需要根据用户点击、图层开关、权限角色变化而动态改变,用路径一。
- 如果要做"建筑橙色选中+边缘发光+半透明呼吸"这种复合效果,用路径二,在材质实例里做全套。
我实际生产中把路径一和路径二做了组合:材质实例负责整体的风格表现,运行时接口负责具体给哪个图层哪个对象下发什么颜色。配置文件那层几乎没再用,主要是怕之后SDK升级覆盖掉自定义配置。
3. 环境坑位:引擎版本、注册表路径与插件识别
改颜色的坑解决之后,还有个更前置的问题经常卡住人:SDK插件能在项目里正常加载和运行,本身就需要环境清干净。这一章把我在UE5环境里遇到的和引擎路径、插件识别的相关问题做个汇总。
3.1 UE引擎安装路径与注册表的关联
SuperMap Hi-Fi 3D SDK for Unreal的安装器在安装插件时,需要自动探测Unreal引擎的安装位置。这个探测动作在Windows上主要依赖注册表。Epic的Unreal引擎安装时,会在注册表写入类似这样的键:
HKEY_LOCAL_MACHINE\SOFTWARE\EpicGames\Unreal Engine\4.0注意这里面的"4.0"并不是特指UE 4.0版本,它更像是Epic保留的一个固定入口,很多插件的安装器都会去这个键下面读BuildDirectory字符串值,拿到引擎实际所在路径,比如D:\Program Files\Epic Games\UE_5.3。
如果你是用Epic Games Launcher正常安装的引擎,这个键一般不会丢。如果你用的是从源码编译的引擎,或从别人那里拷贝的绿色版引擎,这个键就经常缺失,结果就是安装器提示"Unable to find Unreal Engine",插件装不进去,或者装进去了在Editor里显示"Disabled"。
解决方法是检查注册表,缺了就手动补:
打开注册表编辑器 regedit 定位到 HKEY_LOCAL_MACHINE\SOFTWARE\EpicGames\Unreal Engine\4.0 如果没有BuildDirectory字符串值,新建一个,数值数据填引擎根目录,例如 D:\Program Files\Epic Games\UE_5.3还有一点要注意:有些32位的安装器读不到64位注册表视图,导致明明装了引擎还找不到。这时在64位系统上要让安装器以64位模式运行,否则就得在WOW6432Node分支下检查一遍注册表项。这个坑特别隐蔽,因为表面上看注册表路径一模一样,但32位和64位视图是两个独立空间。
3.2 UE5下高亮接口不生效的排查链路
我刚开始在UE5.3上运行时,遇到过选中对象后高亮颜色变了,但过几帧又变回去的现象。排查过程比较典型,也很有代表性:
第一步,确认SDK版本与引擎匹配。很多GIS SDK的发布周期滞后于Unreal官方版本,当时我手里那个SDK插件明确标注支持UE 5.1/5.2,在UE5.3上虽然能编译通过,但内部渲染行为已经出现偏差。后来升级到对应SDK版本才正常。检查方法很简单:打开.uplugin文件看EngineVersion兼容范围。
第二步,排查高亮接口的调用时机。SDK的场景Actor在BeginPlay之后还需要一段时间做数据加载和Mesh生成,如果你在BeginPlay里立刻调用SetHighlightColor,这时内部高亮材质可能还没初始化完成,设置被吞掉。改法是在SDK的OnSceneInitialized或者OnLayerAdded事件回调后再设置。
第三步,检查是否被后处理干扰。如果场景里有PostProcessVolume且启用了Bloom,高亮材质的自发光部分会被泛光放大,颜色看起来饱和度不对。这不算接口失效,只是颜色被后期效果改了。我当时把Bloom强度临时调到0,颜色"偏灰"的问题立刻就暴露出来了。
3.3 一个容易混淆的问题:版权提示和选中高亮无关
在GIS类插件混用的项目里,经常有人问,UE5里Cesium for Unreal场景右下角一直显示Cesium的版权标识,能不能通过设置隐藏掉。这个问题和SuperMap SDK的高亮修改本质无关,但既然做三维GIS集成,迟早会遇到。
我的观点是:SDK和数据的版权标识是合规要求,修改选中高亮颜色不应该也不需要去动版权显示。SuperMap Hi-Fi 3D SDK for Unreal本身在加载服务数据时也可能出现数据来源标识,这是正常现象。如果你为了截图好看想临时隐藏,那是另一回事,但绝不能把"隐藏版权"当成常规功能写进交付文档。渲染层面,版权一般是UI层,和选中高亮的材质通道互不干扰,你改高亮颜色完全不需要管它。
3.4 iServer 11i的数据组织方式对选中高亮有直接影响
数据从SuperMap iServer 11i发布出来,到了SDK这边怎么被组织成可拾取对象,这点特别影响高亮效果。
如果你的数据是模型数据集、白模建筑、管线这类带有明确对象边界的矢量模型,选中高亮是按对象来算的,选中一个楼就是一个楼变高亮,效果干净利落。如果你的数据是倾斜摄影模型、点云或者BIM整体烘焙,那SDK拿到的更多是分层Mesh而不是独立业务对象,这个时候"选中高亮"可能退化成"整层高亮"或"瓦片级高亮",颜色切换非常生硬。
在SuperMap iDesktop里处理数据时,可以提前把需要做对象级选中的模型单独抽出为模型数据集,或者给数据集设置独立的图层风格。这个习惯会让你在后面做高亮业务时省掉一大半力气。iServer 11i发布时,尽量使用S3M格式,它对对象标识的保留比传统切片格式完整得多。
4. 进阶玩法:按模型分类分配不同高亮色与动态效果
基础色改完之后,我们项目的业务场景需要把"选中对象"和"业务类型"绑定起来。比如同一个场景里同时存在建筑、绿地和地下管线,客户要求选中的建筑显示成橙色、绿地显示成绿色、管线显示成荧光黄。这就需要给每个选中对象单独下发颜色。
4.1 按模型属性字段分类下发高亮颜色
SuperMap数据集里的模型一般会带业务字段,比如在iDesktop里给建筑数据集加了"类型"字段,分为住宅、办公楼、学校,那么SDK拾取到对象之后,我们可以通过SDK的查询属性接口拿到这条记录的字段值,再决定用什么颜色。
基本逻辑长这样:
// 伪代码,接口名以当前SDK版本为准 TArray<FSuperMapObjectInfo> SelectedInfos = SelectionManager->GetSelectedObjectInfos(); for (const auto& Info : SelectedInfos) { FString ObjectType = Info.GetFieldValue(TEXT("Type")); FLinearColor TargetColor; if (ObjectType == TEXT("住宅")) TargetColor = FLinearColor(1.0f, 0.5f, 0.0f); else if (ObjectType == TEXT("办公楼")) TargetColor = FLinearColor(0.8f, 0.2f, 0.8f); else if (ObjectType == TEXT("学校")) TargetColor = FLinearColor(0.0f, 0.8f, 1.0f); SelectionManager->SetObjectHighlightColor(Info.ObjectID, TargetColor); }这段代码看起来简单,但要注意一个性能点:如果选中了上百个对象,循环里逐个调SetObjectHighlightColor,底层可能会对Mesh材质参数做多次广播,帧率会有明显掉帧。我的做法是把同一颜色的对象ID收集起来,按颜色批量设置,减少SDK内部的材质参数更新次数。
4.2 叠加呼吸和闪烁效果
为了让评审汇报时重点对象更抢眼,可以在高亮颜色基础上加呼吸效果。呼吸的本质是让透明度或自发光强度随时间周期性变化。用Unreal自带的Timeline组件就能实现。
先给需要呼吸的对象设置高亮材质实例,然后在Tick里修改材质参数:
// 通过Dynamic Material Instance控制呼吸 UMaterialInstanceDynamic* DynMat = MeshComp->CreateAndSetMaterialInstanceDynamic(0); float BreathPhase = FMath::Sin(GetWorld()->GetTimeSeconds() * 2.0f) * 0.5f + 0.5f; DynMat->SetScalarParameterValue(TEXT("HighlightIntensity"), BreathPhase);这里有个小坑:如果直接在SDK的对象上强行创建动态材质实例,SDK下一次高亮状态切换可能会把材质还原成它自己管理的材质。我最终的稳妥方案是,把呼吸效果做成一个独立的材质函数,利用World Position Offset或者Vertex Color通道叠加,同时让SDK在设置高亮时不要覆盖材质参数。具体的"不覆盖"设置,有的SDK版本叫bOverrideMaterialParams,有的叫KeepCustomMaterial,翻头文件找一下即可。
4.3 同屏多对象选中的颜色区分
多选模式下,默认高亮会让所有对象变成同一种颜色,视觉上分不清"当前主选中"和"其他选中"。参考CAD的夹点设计,可以把主选中对象设置成高饱和度颜色,把次要选中对象设置成低透明度、低饱和度的同色系颜色。这样用户一眼就能看出哪个是当前操作的焦点对象。
SDK通常会在选中集合里提供当前"焦点对象"或者"最后选中对象"的标识。如果版本没有直接暴露,你可以自己在业务层记录最后一次点击的对象ID,然后单独给它下发一个更亮的颜色,其余对象用统一色。
5. 改完之后我踩过的坑与验证建议
5.1 颜色改了不生效的几个常见原因
这部分是最有实用价值的,我按实际踩坑频率排个序:
- SDK重置材质参数:SDK在某些状态下(取消选中、重新加载图层、镜头切换)会重置对象材质到默认状态。对策是监听SDK的场景更新事件,在事件回调里重新设置颜色。
- LOD切换导致材质丢失:高LOD和低LOD的Mesh可能使用不同的材质槽,你只在其中一个LOD上改了颜色,镜头拉远切到低LOD时颜色又变回去了。对策是确认你的高亮设置在
All LODs上都生效,或者临时锁定LOD层级排查。 - 色彩空间不一致导致颜色偏灰:Unreal默认是sRGB管线,但部分SDK内部处理颜色时走的是线性空间。你填的
FLinearColor(1.0f, 0.5f, 0.0f)和材质面板里看到的颜色不一定一致,建议用FColor::FromHex配合sRGB转换。 - PostProcess Volume里的Color Grading把颜色带偏了:我在夜景项目中遇到过,后处理里把全局饱和度调低后,高亮颜色也跟着变灰。这不是SDK问题,检查后处理即可。
- 配置文件和代码重复设置:如果配置里写了一种颜色,代码里又调了另一种,以代码为准,但容易造成"改了配置为什么没反应"的错觉。
5.2 性能与效果平衡的几个参数建议
高亮材质如果用了半透明混合,会显著增加OverDraw,尤其是城市级场景里同时选中多个建筑时。我的建议:
- 高亮区域能不用全模型透明就不要全模型透明,优先用边缘发光或描边,降低OverDraw压力。
- 半透明高亮时,
Opacity控制在0.6到0.85之间,太低的话背后物体透出来会显得脏,太高又遮住模型本身纹理。 - 呼吸效果的频率控制在1~2Hz,超过2Hz容易让用户视觉疲劳。
- 如果一帧内要更新大量高亮颜色,先合并对象再批量提交,避免逐个触发材质参数更新。
5.3 从iDesktop到iServer再到UE的一致性验证
高亮颜色虽然主要靠客户端控制,但如果数据在SuperMap iDesktop里设置了特殊的图层风格,发布到iServer 11i之后再被SDK加载,默认高亮层的表现可能和iDesktop预览时不一样。
我的建议是在项目启动阶段做一个"高亮一致性检查":选一个已知对象,分别在iDesktop、WebGL端、UE端各选中一次,把颜色的十六进制值记录下来对比。如果差距大,优先在iServer服务端把图层样式统一,再回到客户端处理业务高亮。这样可以避免"同一个场景在不同端颜色不一致"的验收问题。
5.4 我的几个调试技巧
最后分享几个平时调试高亮问题的技巧,都很简单,但能省很多时间:
- 用
SceneCapture做一个角落小窗口实时预览选中对象,方便在不打断主视图操作的情况下检查颜色效果。 - 在SDK日志级别里开启
LogSuperMapSDK Verbose,高亮切换时会输出对象的Mesh更新信息,能直接看到材质被覆盖的时机。 - 改颜色之前先用Unreal的
Visual Logger或者DrawDebugLine在选中对象周围画一个包围盒,先确认SDK是否真的拾取到了目标对象。很多"高亮颜色不对"其实是因为拾取ID错位,对象都选错了,颜色自然不对。 - 如果是打包后的程序里颜色不对,先在编辑器PIE模式里跑一遍,如果编辑器正常、打包异常,优先检查配置文件的打包规则,确保
Config目录被包含进Pak。
改选中高亮这个事,看着是个小功能,实际上牵扯到SDK渲染机制、数据组织方式、引擎环境配置和后处理合成好几层。我现在再遇到类似需求,已经习惯了按"先确认拾取对象、再分优先级设置颜色、最后验证LOD和后处理"的流程走,几乎不会再被"改了半天不生效"卡住。如果看完这篇你还是没找到自己项目里的问题,建议直接从5.1的五个原因清单开始排查,大概率是其中之一。