ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

无建筑数据下数字孪生场景:GIS+UE程序化生成周边建筑全流程

无建筑数据下数字孪生场景:GIS+UE程序化生成周边建筑全流程 上个月接了个数字孪生项目对方给了一块待建新区的范围要求把区域内的建筑、道路、地块关系全部做出来。结果一开数据包建筑只有二十来栋剩下几百栋全是空白。项目方也很坦诚这个区域处于规划中后期建筑方案一直在调整BIM模型还没定版只有地形、路网和地块边界。这套情况在数字孪生行业里太常见了——客户要的是整个区域的未来场景可眼下能给的只有GIS基础数据。那怎么办只能靠GIS数据打底结合UE的程序化生成能力把尚未有建筑数据的区域快速铺满可信度足够的周边建筑。这篇文章就聊聊我在这类项目里沉淀下来的一套流程无建筑数据区域里怎么从GIS信息出发在UE里快速生成数字孪生场景的周边建筑。整套流程涉及坐标转换、数据抽取、程序化建模、性能优化几条线每一步都有替代方案也都有必须避开的坑。我会按实际执行顺序写尽量把为什么这么做、踩过什么坑都讲清楚希望帮到正好卡在这个环节的人。1. 先想清楚没有建筑数据并不是死路一条很多刚接触数字孪生的人一听说没有建筑数据就觉得项目没法做。这个判断过于绝对。数字孪生场景的价值分很多层决策层看的是城市空间结构、地块开发强度、视线通廊、路网关系招商展示层看的是这里未来的城市风貌长什么样只有到了工程审批、施工模拟这种层面才真的需要精确到幕墙分格、结构柱网的BIM级模型。前两者完全可以依靠 GIS 数据配合程序化生成来解决。1.1 这个需求在数字孪生项目里有多常见可以说只要项目边界不是一个已经建成的成熟CBD基本都会遇到建筑数据不全的情况。我把近几年接触过的项目归了几类新城区、开发区、产业园区规划在调整只有地块控规没有建筑方案旧城更新片区现状建筑普查数据只到街区级别没细化到单栋建筑大型基础设施影响区如高铁站周边、机场搬迁区建筑处于拆建交替状态文旅景区、乡村振兴区域大量宅基地、自建房既有GIS数据里根本没有建筑矢量在这些场景里如果死等建筑数据到位再开工项目周期根本不现实。反过来用GIS里的地块边界、容积率、建筑限高、路网退距等信息完全可以先把规划态的周边建筑生成出来——这里的周边建筑本来就应该是规划后的效果而不是现状。1.2 三条可落地的技术路线对比无数据生成建筑业内主流路线有三条我做个对比方案数据依赖精度速度适用场景基于OSM/测绘轮廓拉伸体块建筑轮廓层数中快现状建成区快速底模遥感影像深度学习提取屋顶高分影像中高中有影像但无矢量数据的区域地块控规程序化生成地块边界容积率/限高中低极快规划新区、待建区方案一适合建筑已经建起来但没矢量化的区域方案二适合有遥感影像、需要更真实轮廓的场景方案三则完全适用于标题说的这个场景——没有任何建筑数据只有GIS地块信息。我这次项目采用的就是方案三这也是本文主要展开的路线。1.3 如何判断该选哪条路判断标准其实就一个你的周边建筑是陪衬还是主角。如果是项目核心区域周边用来烘托氛围的楼群方案三完全够用——它们只需要在鸟瞰视角下形成合理的城市肌理。如果某栋建筑本身是重点展示对象则在生成体块后单独替换为手工模型或精模。提示做数字孪生周边建筑目的不是还原每栋楼的真实长相而是让整个场景在空间尺度上可信。可信三个字是这条技术路线的核心标准。2. 坐标底子从GIS经纬度到UE场景坐标的换算链路这一节是整个流程里最容易被忽略、但出问题最严重的部分。GIS数据用的是经纬度WGS84坐标系而UE的场景坐标是右手坐标系单位是厘米Z轴朝上。如果直接硬转或者不负责任地大概对齐生成的建筑位置可能偏差几十米整个场景就废了。2.1 为什么不能直接把经纬度当成UE坐标有人会想我直接把经度当成X纬度当成Y拉到UE里不就行了但这样做有两个致命问题经纬度是角度的度不是米。1度经度在赤道约111公里在纬度60度的地方只有约55公里纬度方向虽然相对稳定但平面的X/Y无法直接对应米制长度。直接把度当坐标UE里一个单位的长度含义完全混乱。UE对浮点数精度有严格要求大坐标下物体渲染会出现抖动。数字孪生项目通常以真实经纬度来定位如果场景原点离坐标值太大比如上万米视角一拉远模型就开始呼吸式抖动。所以正确做法是选定一个场景局部原点把真实地理坐标换算成相对原点的局部坐标。2.2 场景原点怎么选场景原点最好选在项目区域的几何中心或者客户指定的某个地标点比如区域中心广场、主要路口。我一般直接在GIS里查看项目范围的最大最小经纬度取平均值作为中心点。中心经度 (max_lon min_lon) / 2 中心纬度 (max_lat min_lat) / 2这个中心点将成为UE世界坐标的 (0,0,0) 附近。所有建筑、道路、地块的坐标都相对这个点计算。这样处理局部坐标值一般能控制在几公里以内UE的精度压力就小很多。2.3 手写坐标换算从WGS84到局部平面坐标把经纬度转成米制局部坐标我常用的工具是Python的pyproj库。核心思路是用UTM投影或者自定义横轴墨卡托投影把WGS84经纬度转成米再减去原点的米制坐标。from pyproj import Transformer # UTM分区需要根据项目经度选择中国大部分区域在49N/50N/51N transformer Transformer.from_crs(EPSG:4326, EPSG:32650, always_xyTrue) origin_lon, origin_lat 116.3912, 39.9072 # 示例中心点 def lonlat_to_local(lon, lat): x, y transformer.transform(lon, lat) ox, oy transformer.transform(origin_lon, origin_lat) local_x (x - ox) * 100.0 # 米 - UE厘米 local_y (y - oy) * 100.0 return local_x, local_y有几点需要特别提醒关于UTM分区如果项目范围横跨两个UTM分区或者接近分区边界直接用UTM会出现几十米的误差。这种情况我建议用自定义投影或者改用Web Mercator做近似虽然Web Mercator有距离变形但在城市级小范围误差可以接受而且处理起来更简单。关于轴向对齐UTM的X轴指向东Y轴指向北。UE中X轴可以对应东Y轴则要对应北。如果直接把UTM的X/Y灌进UE的X/Y你会发现场景被镜像了——因为UE的Y轴方向和GIS的北方在视觉上不一定对应你的预期。最简单的方法是换算后做一次绕Z轴的旋转校准或者交换X/Y再根据项目朝向调整。关于高程如果项目区域有起伏地形建筑底部必须贴合DEM。我在项目里会把DEM转成Heightmap导入UE的地形系统再在生成建筑时读取该位置的Z值作为建筑底面的高程。2.4 高程怎么接DEM与地形对齐建筑不是贴在地上的贴纸地面有起伏时建筑底面必须长在地形上。我的做法分三步取项目范围的DEM数据SRTM 30米分辨率或者ALOS 12.5米导出为TIFF在GIS里把DEM重采样输出成16bit灰度图按UE地形高度范围做映射UE地形系统导入后每个建筑轮廓点所在位置读取地形高度值这里面最容易出问题的是DEM和影像/矢量数据坐标系不一致。导入UE前务必在GIS里统一所有图层的坐标系我一般统一到WGS84 UTM再用前面那段Python脚本转换成UE局部坐标。这一步做好后面生成建筑时建筑底部高度就能直接从地形Sample出来不会出现楼飘在半空或者楼陷进地里的尴尬。3. 建筑轮廓从哪来OSM下载、影像提取与人工兜底坐标链路打通后需要解决建筑轮廓这个核心输入。即使没有官方建筑数据也并非完全没有轮廓来源。我按可靠度从高到低排序讲。3.1 OSM数据下载与字段解析OpenStreetMap在国内部分城市的数据覆盖已经不错尤其是城市建成区建筑轮廓数据也相对完整。虽然不能直接用在一线城市核心地段建筑太密、争夺严重但用在新城、郊区的周边场景铺底够用。OSM建筑数据的获取方式最常用的是Overpass API[out:json][timeout:25]; area[name某新区]-.a; ( way[building](area.a); relation[building](area.a); ); out body; ; out skel qt;返回的GeoJSON里每个建筑的way包含了轮廓节点坐标和一组标签。常用标签有buildingyes表示这是一个建筑building:levels层数决定体块高度building:height总高度米优先于levelsroof:shape屋顶类型gabled、hipped、flat等building:colour/roof:colour颜色这里有个关键经验OSM建筑轮廓通常比实际建筑大一圈。因为OSM的勾绘者多是基于卫星影像人工描边边缘误差一到两米很正常。如果只是用于底模氛围无所谓但要做近景展示应该对轮廓做一次向内收缩buffer负值让体块尺寸更接近真实。3.2 轮廓清洗重叠面、短边、破损面的处理从OSM拉下来的数据不会直接可用大部分要过一遍清洗流程。我做清洗时按以下顺序处理去除重复面多条way描述同一建筑需要按坐标相似度去重去除碎片面面积小于20平方米的轮廓直接丢弃那通常是院落或构筑物去除过长边/错误闭环有些轮廓点顺序错误导致孔洞或自相交简化顶点一栋矩形建筑可能有几十个点做一次Douglas-Peucker简化把顶点数压到最少的合法多边形修复自相交用JTS/GEOS库做buffer(0)操作可以自动修复部分自相交问题在GIS里这一步可以用QGIS的处理工具箱一键跑完。在代码里则可以用Python的shapely库做类似处理。如果项目数据量大建议在Python里预处理完再输出给UE不要在UE里做拓扑修复。3.3 轮廓转成UE可读的数据格式清洗后的建筑轮廓我一般导出成CSV或JSON文件结构示意如下build_id,coord_count,coords(JSON),levels,height,roof_type B0001,4,[[116.3912,39.9072],[116.3915,39.9072],[116.3915,39.9075],[116.3912,39.9075]],6,22,gabled B0002,5,[[116.3920,39.9080],...],3,11,flat然后在UE里用DataTable读入每一行就是一条待生成的建筑记录。这里的字段越精简越好——UE侧只关心轮廓点坐标、层数/高度、屋顶类型其他信息没必要带过去。注意CSV里每栋建筑的坐标点顺序必须保证是顺时针或逆时针统一UE端做多边形三角剖分时才能得到正确结果。我习惯统一存成外环逆时针这能避免很多三角剖分时的孔洞问题。如果项目区域连OSM数据都没有比如真正的待建新区建筑轮廓完全不存在那就只能走下一层方案用地块边界代替建筑轮廓。4. UE里批量生成建筑体块体块、贴图与LOD轮廓数据和坐标系统都到位后核心的UE侧生成工作就开始了。这一步是整个流程里工程量最大的部分也是最能体现经验差的地方——同样的数据有人生成出来是一排排规矩的吐司面包有人生成出来是一片有生活气息的城市街区差别全在细节处理上。4.1 生成逻辑地块边界规划指标随机种子对于没有建筑轮廓的新区我用的是地块推演思路。每个GIS地块里根据常规规划逻辑推导建筑体块读入地块边界、容积率、建筑限高、建筑密度这几个控规指标在地块内按照退距要求一般默认建筑距地块边界5~10米生成建筑的平面占位根据容积率反推建筑层数沿主要道路一侧排布裙房或主楼通过随机种子控制每栋建筑的细微差异轮廓偏移、屋顶形式、立面纹理这一步如果用代码实现逻辑不复杂。但要注意地块推演生成的不是现状而是规划逻辑下的合理推测。所以生成的建筑会有明显的街区感沿街连续、中心围合、高度渐变——这些特征都是规划控规里能读出来的。UE里的落地方式有两个主流选择一个是直接用蓝图Spline建轮廓后手动挤出另一个是调用UE5的Geometry Script插件用节点化方式做多边形挤出。我用后者比较多原因是Geometry Script在蓝图里就能实现读入坐标数组→生成多边形→挤出成体块不需要写C就能完成整个生成流程迭代速度非常快。4.2 体块生成实操Geometry Script生成可编辑网格UE5自带Geometry Script节点库提供了Create Polygon、Extrude等核心节点。我封装的生成逻辑大致是根据轮廓点数组调用Create Polygon生成一个平面多边形用Extrude Mesh节点把这个平面沿Z轴挤出挤出高度根据层数和层高算出然后针对屋顶类型做进一步处理平屋顶直接封顶有檐口做水平外扩坡屋顶则在上方再叠加一个楔形体最后用Transform Mesh把整个体块放置到该建筑对应的局部坐标上每栋楼生成完毕后就是一个独立的Dynamic Mesh可以后续转成Static Mesh保存。生成完不等于能用。这里必须再过一个优化环节把建筑按类型分组相同尺寸、相同层数的建筑尽可能共用网格资源用实例化绘制ISM/HISM的方式渲染。几千栋建筑如果每栋都是一个独立的StaticMeshActorDrawCall会直接爆掉。用实例化之后同类型建筑的DrawCall可以合到个位数。4.3 立面贴图与屋顶细节位置和体块对了一眼看去还是白模的感觉不够真实。想让场景快速摆脱积木感关键是立面贴图的处理技巧。立面贴图我一般准备一套2~4层的建筑立面素材而不是一栋楼一张图。素材里包含窗户、阳台、空调位、底层商铺等常见元素。在UE材质里用水平方向按建筑长度做Tiling垂直方向结合楼层数量做重复采样。这样一栋6层的楼和15层的楼虽然用同一张贴图但呈现出来的立面节奏完全不同。屋顶细节是很多人会漏掉的点。从鸟瞰视角看屋顶面积占比很大。如果所有屋顶都是纯色平台场景的真实感会明显打折。我的做法是平屋顶叠加设备间、电梯机房、女儿墙坡屋顶则做瓦片排布贴图。这些细节不需要单独建模用贴图和法线就能骗过大多数人的眼睛。随机性是让假建筑看起来真的最后一环。每次生成时在同类型贴图里随机选一张在Tiling数量上做10%左右浮动建筑朝向允许有微小的偏转——这些细微差异叠加起来就能让同一块区域里的建筑呈现每栋都不一样的观感而不是CtrlC/CtrlV复制出来的碉堡群。5. 让假建筑不穿帮细节处理与性能平衡最后这节聊一些让整条管线真正可用、可交付的经验。前面几节解决了有没有的问题这一节解决的是能不能用、会不会翻车的问题。5.1 远看是城市近看是积木——LOD策略程序化生成的建筑不可能在任何距离都保持同样的细节。近景看立面需要窗户分隔远景看只需要一个色块轮廓。我的LOD策略分三层距离表现方式面数控制0~200米完整体块立面贴图每栋2000~4000面200~800米简化体块去掉屋顶细节每栋200~500面800米以上Imposter替身或纯色快每栋1~4面在UE里我用Nanite承接近景建筑网格远景则用普通StaticMeshHLOD自动合并。Nanite支持自定义材质和贴图但对动态生成的Procedural Mesh支持有所限制UE 5.3后逐步放开所以如果项目版本不够新近景建议用StaticMesh烘焙后的结果不要直接依赖Nanite处理动态网格。5.2 性能实测数据参考我之前一个测试场景是4公里乘4公里的新区生成约1800栋建筑在关闭Nanite、全部走HISM实例化的前提下建筑侧总DrawCall稳定在120~150之间在移动端中端设备上能保持30帧以上。如果是PC端开Nanite三角形数量飙升但GPU在现代显卡上完全可以扛住。所以性能优化策略要分平台来定不能一刀切。平台推荐做法预估建筑容量移动端HISM实例化2级LOD5000栋以内PC端中端Nanite近景HISM远景20000栋以内PC端高性能Nanite全量50000栋以上5.3 我在实践中踩过的四个坑这一路踩过的坑不少挑四个最典型的展开讲讲。坑一建筑整体悬空或陷地。一开始我在生成建筑时以几何中心作为放置锚点结果地势起伏稍大的地方建筑全都半个身子埋在地里或者悬在半空。后来改成读取地形高度取建筑底面最低点对齐地形。但这里又有个新问题——比如一个跨在两片坡地上的长条形建筑底面高度取哪个点都不合适。最终方案是把建筑轮廓细分成多个小段每段读取独立的高度值用这些高度值的平均作为建筑基准面再结合轮廓的跨度做适当整体抬高。坑二建筑方向镜像。由于GIS坐标系的X/Y方向和UE的正方向存在差异第一次生成出来的建筑全部沿着一条镜像轴反转了。排查了半天最后确认是UTM的Y轴向北UE的Y轴方向在特定配置下会朝南取决于相机朝向约定。修正方式也很简单换算坐标时对调X/Y再做一次90度旋转校准。坑三导入OSM数据后多出大量重叠碎面。OSM的way数据经常会因为人工描绘导致同一个建筑出现多个重叠面。如果不清洗就全量导入UE生成的建筑会互相穿插。我后来养成了一个习惯所有OSM数据都先过一遍buffer(0)再合并相交面彻底消除重叠。坑四远景建筑Poping问题。LOD切换的一瞬间建筑轮廓突然变化很影响视觉体验。UE里我用了Dithered Opacity Transition抖动物体透明度过渡来处理LOD切换效果不错。代价是会增加一定的渲染压力需要在项目性能预算之内权衡。5.4 什么时候该升级成精细模型也要说句公道话程序化生成不是万能的。遇到以下情况还是要安排美术同学手工建模数字孪生场景中的核心建筑比如项目重点展示的产业园主楼、标志性超高层客户明确要求做室内穿透展示的建筑建筑外观高度依赖品牌识别比如商场、酒店、大型公建距离人视角近距离漫游可达的建筑外立面我的原则是周边建筑用程序化生成核心建筑用精模替换。两套内容在UE里按ID对应精模到位后直接替换替换时保持坐标、旋转、缩放一致就不会影响整体场景。最后分享一点实际操作中的感受这套管线跑通的第一个里程碑不是生成出第一栋建筑而是整个工作流能反复、批量、稳定地重跑。项目初期我用固定参数跑了一遍效果还行后来地块控规调整了容积率、限高都变了我改完GIS表格里的数据重新跑一遍程序化生成流程五分钟就得到了新方案下的城市布局。那时候我才意识到这条路线的真正价值不是省下了从零建模的那几周时间而是让建筑数据变动本身变成了一个低成本的常态迭代——数字孪生项目最怕的不是数据不准而是数据一变动就要全部返工。现在这样改数据结构、改指标参数、重新出图一整套流程都在掌控之内客户再提出这版建筑高度太压抑或者临街界面再丰富一点都只是调参和微调的事不再需要从三维建模环节重新来过。
返回列表