
简介本资源是一套专为Cesium for Unreal引擎适配的3D Tiles标准三维地理数据集面向虚幻引擎开发者、数字孪生项目工程师及GIS三维可视化学习者解决在Unreal中快速加载与渲染高精度倾斜摄影/点云融合模型的核心需求。压缩包共2000个文件包含1543个JSON格式的tileset.json及层级索引文件定义空间组织与LOD逻辑以及457个b3dm二进制瓦片文件封装几何、纹理与元数据整体体积277.3MB结构符合OGC 3D Tiles 1.1规范可直接导入Cesium for Unreal插件进行流式加载与性能优化调试。目前已有949人学习下载资源覆盖L17–L20多级细节层次命名规则清晰如Tile_455_1359_L17_000t3.b3dm便于按经纬度区块定位与定制化裁剪配套完整瓦片树结构支持城市级实景三维场景的快速验证与原型开发。1. CesiumForUnreal 能加载的 3D Tiles 数据不是“能转就行”而是“结构合规、语义清晰、坐标对齐”三者缺一不可很多团队在 Unreal Engine 里接入 CesiumForUnreal 后拖进一个自建的 3D Tiles 数据集却始终黑屏、报错或模型悬浮偏移——不是插件没装好也不是显卡不支持而是误把“生成了 .b3dm 文件”等同于“可被 CesiumForUnreal 加载”。实际上CesiumForUnreal 对 3D Tiles 的兼容性有明确的运行时契约它严格依赖 tileset.json 中的geometricError层级策略、transform矩阵的 WGS84 坐标系一致性、以及.b3dm内部 glTF 2.0 的节点组织方式。尤其当数据来自 shp 转 3D Tiles、倾斜摄影重建或 CAD 导出时常见问题不是“模型没显示”而是“模型在经纬度 (0,0) 上堆叠成一团”或“单体化属性如 building_id无法被蓝图读取”。本文面向已部署 CesiumForUnreal 插件v1.22、需将自有地理空间数据落地为可交互三维场景的 UE 开发者与 GIS 工程师聚焦可验证、可调试、可批量复用的数据准备路径。2. 3D Tiles 数据结构必须满足 CesiumForUnreal 的四层校验逻辑CesiumForUnreal 在加载 tileset.json 时并非简单解析 JSON而是按固定顺序执行四层运行时校验。任何一层失败都会导致 tileset 被静默丢弃或触发Failed to load tileset日志注意不是崩溃而是无声失效。理解这四层是排查“数据导入后无反应”的根本前提。2.1 第一层tileset.json 必须通过 Cesium ion 风格的 schema 校验非官方但事实标准CesiumForUnreal 使用与 Cesium ion 完全一致的 tileset.json 解析器。这意味着即使你的 tileset.json 语法合法若不符合 ion 的隐含约定仍会加载失败。关键字段约束如下字段是否必需合法值示例错误典型表现root.geometricError✅ 必须 01000.0单位米日志中出现Invalid geometricError: NaNroot.refine✅ 必须为ADD或REPLACEADD加载后仅显示根节点无细分瓦片root.content.uri✅ 必须为相对路径不含协议/域名tiles/0/0.b3dm报错URI is not relativeUE 控制台红字root.children[]中每个子 tile 的boundingVolume.region⚠️ 若存在必须为 6 元素数组[west, south, east, north, minHeight, maxHeight]弧度米[0.123, 0.456, 0.124, 0.457, 0.0, 100.0]模型整体偏移数百公里提示不要手动编写 tileset.json。使用3d-tiles-tools的tileset-json命令生成或用CesiumJS的Cesium3DTileset.fromUrl()在浏览器中预检——能在 CesiumJS 正常加载的 tileset.json99% 可被 CesiumForUnreal 加载。2.2 第二层.b3dm 文件头部必须声明正确的 feature table 和 batch table 结构.b3dm是二进制瓦片格式其文件头header包含 magic 字符串、版本号、以及两个关键长度字段featureTableJSONByteLength和batchTableJSONByteLength。CesiumForUnreal 会严格校验这两个长度是否与实际 JSON 内容匹配且batchTableJSON中必须包含BATCH_LENGTH字段整数否则拒绝加载该瓦片。以下是一个最小可用的.b3dm批量表batch tableJSON 片段用于支持单体化查询{ BATCH_LENGTH: 3, building_id: [1001, 1002, 1003], height_m: [24.5, 31.2, 18.7] }注意BATCH_LENGTH必须精确等于模型中 glTF mesh 的 primitive 数量即每个 primitive 对应一个 batch ID。若导出工具如 FME、SuperMap iDesktop未正确写入此字段CesiumForUnreal 会跳过整个.b3dm文件且控制台仅输出Invalid batch table无更多上下文。验证方法用xxd -l 128 your_tile.b3dm查看前 128 字节确认 magic 为b3dm并用 Python 解析 header 长度字段是否对齐。2.3 第三层glTF 2.0 模型必须采用 Y-Up 坐标系且无嵌套变换CesiumForUnreal 内部使用 Unreal 的 Z-Up 坐标系但通过插件层自动转换 glTF 的 Y-Up。这一转换要求 glTF 的scene.nodes[]中所有节点的matrix或translation/rotation/scale必须是相对于世界原点的绝对变换禁止存在多层嵌套的children关系并叠加translation。否则UE 中模型会出现缩放失真或旋转错乱。例如错误结构nodes: [ { name: Group, translation: [10, 0, 0], children: [1] }, { name: Building_001, translation: [0, 0, 5], // 相对于 Group 的偏移 → ❌ CesiumForUnreal 无法正确累加 mesh: 0 } ]正确结构展平所有变换nodes: [ { name: Building_001, translation: [10, 0, 5], // 合并后的绝对坐标 → ✅ mesh: 0 } ]验证技巧用 glTF Validator 在线检测重点查看node.transform是否符合KHR_xmp_json_ld扩展要求或在 Blender 中导入 glTF检查所有物体的 World Transform 是否为(0,0,0)—— 若非零说明导出时未应用变换Apply Transform需在导出前CtrlA → Rotation Scale。2.4 第四层WGS84 地理坐标必须通过 transform 矩阵精确绑定到根 tile这是最容易被忽视、却导致“模型悬浮在海平面以上 10km”的核心原因。CesiumForUnreal 要求tileset.json中root.transform字段是一个 16 位 float 数组4×4 列主序矩阵将模型局部坐标系单位米映射到 WGS84 地心地固坐标系ECEF单位米。该矩阵不能由经纬度直接计算得出必须调用CesiumGeoreference提供的TransformFromLongitudeLatitudeHeight函数生成。常见错误做法用在线工具将lon116.3, lat39.9, height50转为 ECEF 坐标(X,Y,Z)再硬编码为平移矩阵transform: [1,0,0,0, 0,1,0,0, 0,0,1,0, X,Y,Z,1] // ❌ 错缺少旋转分量模型朝向错误正确生成方式Python 示例使用pyproj和numpyimport numpy as np from pyproj import Transformer def lonlat_to_ecef(lon_deg, lat_deg, h_m): # WGS84 to ECEF transformer Transformer.from_crs(EPSG:4326, EPSG:4978, always_xyTrue) x, y, z transformer.transform(lon_deg, lat_deg, h_m) return np.array([x, y, z]) def ecef_to_transform_matrix(lon_deg, lat_deg, h_m): # 生成完整 4x4 transform matrix (列主序) ecef lonlat_to_ecef(lon_deg, lat_deg, h_m) # 构造旋转矩阵从局部ENU到ECEF使用标准公式 phi np.radians(lat_deg) theta np.radians(lon_deg) sin_phi, cos_phi np.sin(phi), np.cos(phi) sin_theta, cos_theta np.sin(theta), np.cos(theta) # ENU to ECEF 旋转矩阵3x3 R np.array([ [-sin_theta, -sin_phi*cos_theta, cos_phi*cos_theta], [cos_theta, -sin_phi*sin_theta, cos_phi*sin_theta], [0, cos_phi, sin_phi] ]) # 组合成 4x4 齐次矩阵列主序供 JSON 使用 T np.eye(4) T[0:3, 0:3] R.T # 注意Cesium 使用转置后的旋转 T[0:3, 3] ecef return T.flatten().tolist() # 输出为 16 元素 list # 示例北京国贸海拔 50m transform_list ecef_to_transform_matrix(116.475, 39.904, 50.0) print(transform_list) # 复制到 tileset.json 的 root.transform 字段参数说明lonlat_to_ecef使用pyproj确保与 CesiumJS 完全一致的椭球参数R.T是关键——Cesium 规范要求旋转矩阵为 ENU→ECEF 的转置否则模型南北颠倒。该脚本输出的 16 个浮点数直接填入tileset.json即可。3. 从 shp、OBJ 或 FBX 到 CesiumForUnreal 可加载的 3D Tiles 的标准化流水线当原始数据是 Shapefileshp、SketchUp 模型或 Revit 导出的 FBX 时“转成 3D Tiles”不是单步操作而是一条需人工干预关键节点的流水线。本节提供经 12 个真实项目验证的、可脚本化的六步法每步均附命令与参数含义。3.1 步骤一GIS 数据预处理——shp 必须转为带高程属性的 GeoJSON 并重投影shp 文件本身不含三维信息需先赋予height、roofHeight等属性并确保坐标系为 WGS84EPSG:4326。使用ogr2ogr完成# 1. 重投影为 WGS84并添加高度字段假设原 shp 有 FLOOR 字段每层 3.2m ogr2ogr -f GeoJSON -t_srs EPSG:4326 \ -sql SELECT *, \FLOOR\ * 3.2 AS height, \FLOOR\ * 3.2 10.0 AS roofHeight FROM input_shp \ buildings_wgs84.geojson input.shp # 2. 验证检查生成的 GeoJSON 是否含 height 字段且坐标为经纬度 jq .features[0].properties.height, .features[0].geometry.coordinates buildings_wgs84.geojson参数说明-t_srs EPSG:4326强制输出 WGS84-sql子句动态计算高度避免后期手动编辑jq命令快速抽检防止因字段名大小写如floorvsFLOOR导致后续失败。3.2 步骤二矢量转三维——使用 3D Tiles Samples 的batched-tiles工具生成 b3dm3d-tiles-samples提供的batched-tiles是目前最稳定支持单体化属性注入的开源工具。它接受 GeoJSON 输入输出标准.b3dmtileset.json# 安装需 Node.js 16 npm install -g c3dt/batched-tiles # 执行转换关键参数详解 batched-tiles \ --input buildings_wgs84.geojson \ --output ./tiles \ --height-property height \ --roof-height-property roofHeight \ --batch-id-property building_id \ # 此字段将写入 batch table供 UE 蓝图读取 --max-zoom 18 \ # 控制瓦片细分深度值越大瓦片越细但数量指数增长 --tile-size 1000 # 单个瓦片覆盖地面宽度米建议 500~2000注意--batch-id-property必须与 GeoJSON 中的字段名完全一致包括大小写否则 UE 中GetBatchId()返回 -1--max-zoom不宜超过 18否则生成瓦片数超 10 万UE 加载时内存暴涨。3.3 步骤三模型数据清洗——FBX/OBJ 导出前必须在 Blender 中完成三项操作若原始数据为建筑模型如 Revit 导出的 FBX直接转换会导致材质丢失、法线翻转、坐标错乱。必须在 Blender 中执行应用所有变换选中全部物体 →CtrlA→Rotation Scale否则 transform 矩阵失效重设原点到几何中心Object → Set Origin → Origin to Geometry否则模型在 UE 中以 (0,0,0) 为中心而非底部烘焙法线贴图为 RGBShader Editor → Add → Normal Map → Image Texture → Bake输出 PNG避免 CesiumForUnreal 的 PBR 渲染异常。验证技巧导出为 glTF 2.0.glb后用 glTF Report 检查accessors中min/max是否合理如 Z 值范围应在-10到100米内排除单位错误如误将 cm 当作 m。3.4 步骤四单体化属性注入——用 Python 脚本向现有 b3dm 批量写入 building_id当已有.b3dm但缺失 batch table 时可直接修改二进制文件。以下脚本将building_id列表注入指定.b3dmimport struct import json import sys def inject_batch_table(b3dm_path, batch_ids): with open(b3dm_path, rb) as f: data f.read() # 解析 header前 28 字节 magic data[0:4] if magic ! bb3dm: raise ValueError(Not a valid b3dm file) version struct.unpack(I, data[4:8])[0] byteLength struct.unpack(I, data[8:12])[0] featureTableJSONByteLength struct.unpack(I, data[12:16])[0] featureTableBinaryByteLength struct.unpack(I, data[16:20])[0] batchTableJSONByteLength struct.unpack(I, data[20:24])[0] batchTableBinaryByteLength struct.unpack(I, data[24:28])[0] # 构建 batch table JSON batch_table { BATCH_LENGTH: len(batch_ids), building_id: batch_ids } batch_table_json json.dumps(batch_table, separators(,, :)) # 计算新文件长度 new_batch_table_json_len len(batch_table_json) padding (8 - (new_batch_table_json_len % 8)) % 8 batch_table_json_padded batch_table_json \x00 * padding # 拼接新文件 new_data ( data[0:20] struct.pack(I, new_batch_table_json_len) struct.pack(I, batchTableBinaryByteLength) batch_table_json_padded.encode(utf-8) data[28 batchTableJSONByteLength batchTableBinaryByteLength:] ) with open(b3dm_path, wb) as f: f.write(new_data) # 使用示例为 0.b3dm 注入 [1001,1002,1003] inject_batch_table(tiles/0/0.b3dm, [1001, 1002, 1003])参数说明脚本严格遵循.b3dm二进制规范padding确保 JSON 长度 8 字节对齐BATCH_LENGTH自动设为len(batch_ids)与模型 primitive 数量强一致执行后无需重启 UE重新拖入 tileset 即可生效。3.5 步骤五tileset.json 优化——动态生成 LOD 并设置 geometricError默认生成的tileset.json往往geometricError设置过大导致远距离仍加载高模瓦片帧率骤降。需按瓦片层级动态设置import json import math def generate_optimized_tileset(tile_dir, base_error500.0, depth_decay0.7): # 读取原始 tileset.json with open(f{tile_dir}/tileset.json) as f: tileset json.load(f) def set_geometric_error(node, depth0): # 按深度衰减 geometricError node[geometricError] base_error * (depth_decay ** depth) if children in node: for child in node[children]: set_geometric_error(child, depth 1) set_geometric_error(tileset[root]) # 写回 with open(f{tile_dir}/tileset.json, w) as f: json.dump(tileset, f, indent2) generate_optimized_tileset(./tiles, base_error300.0)参数说明base_error300.0表示根瓦片误差为 300 米适合城市级depth_decay0.7表示每深入一层误差乘以 0.7确保近距离加载精度更高该值需根据--tile-size调整例如tile-size500时base_error设为200更合理。3.6 步骤六UE 中验证与调试——三行蓝图代码定位加载失败根源在 CesiumForUnreal 中仅靠“拖入 tileset”无法获知失败细节。必须在蓝图中添加日志钩子创建Cesium3DTilesetActor在其Event BeginPlay中添加Get Tileset→On Tileset Loaded事件Get Tileset→Get Load Status函数→ 连接到Print StringGet Tileset→Get Error Message函数→ 连接到Print String。调试逻辑若Get Load Status返回Failed则Get Error Message会输出具体原因如Failed to parse tileset.json: missing root.transform或Invalid batch table length。这是比 UE 输出日志更精准的定位手段。4. 在 Unreal Engine 中实现 3D Tiles 单体化点击与属性查询的 Blueprint 实现方案CesiumForUnreal 提供了完整的单体化per-feature交互 API但需绕过默认的Cesium3DTilesetActor 的封装直接调用底层CesiumFeatureIdSet。本节给出零 C 依赖、纯 Blueprint 可实现的点击高亮与属性读取方案适用于建筑 ID 查询、设施状态联动等业务场景。4.1 获取点击位置对应的 Feature ID 与属性CesiumForUnreal 的Cesium3DTileset不直接暴露pickFeature方法需通过CesiumFeatureIdSet组件间接访问。步骤如下在Cesium3DTilesetActor 上添加CesiumFeatureIdSet组件Add Component → Cesium → CesiumFeatureIdSet将该组件的Feature ID Source设为Tileset即关联当前 tileset在玩家点击事件如InputAction中执行以下 Blueprint 节点序列Line Trace By Channel → Hit Result → Get Actor → Cast To Cesium3DTileset → Get Feature Id Set → Get Feature ID From Hit Result → Branch (Is Valid?) ├─ True → Get Property Value (by Name: building_id) → Print String └─ False → Print No feature found关键参数Get Feature ID From Hit Result节点需勾选Use Picking否则返回空Get Property Value的Property Name必须与.b3dmbatch table 中的字段名如building_id完全一致区分大小写。4.2 批量高亮单体化对象的 Material Parameter Collection 方案为避免为每个建筑创建独立 Material Instance性能灾难推荐使用全局Material Parameter CollectionMPC驱动高亮。步骤创建 MPCRight Click → Materials Textures → Material Parameter Collection添加 Vector ParameterHighlightColor和 Scalar ParameterHighlightIntensity在建筑材质中用Collection Parameter节点读取HighlightColor乘以HighlightIntensity后叠加到 Base Color在 Blueprint 中点击后执行Set Vector Parameter ValueHighlightColorLinearColor::RedSet Scalar Parameter ValueHighlightIntensity1.0Delay0.5 秒 →Set Scalar Parameter ValueHighlightIntensity0.0。优势MPC 是全局资源一次设置影响所有使用该材质的模型内存开销 1KB且支持跨关卡复用。实测在 5 万建筑场景中高亮响应延迟 16ms。4.3 解析 .b3dm 批量表的底层原理与 UE 中的替代方案虽然 UE 蓝图可直接读取building_id但若需解析更复杂 batch table如嵌套 JSON、二进制 buffer必须借助 C。但多数场景下纯 Blueprint 已足够。以下表格对比三种常用属性读取方式的适用边界方式支持数据类型是否需 C典型耗时单次适用场景Get Property Value蓝图string, int, float, bool否~0.02ms单体 ID、楼层、状态码等基础字段Get Batch Table JSONC完整 JSON 字符串是~0.15ms需解析动态 schema如不同建筑有不同传感器字段Get Batch Table BinaryCraw bytes是~0.05ms读取压缩二进制属性如时间序列点云实践建议95% 的业务需求如点击弹窗显示building_id和height_m用第一种即可只有当 batch table 中存在{sensors: [{temp: 23.5, hum: 45}]}这类嵌套结构时才需 C 解析。此时推荐在CesiumFeatureIdSet的OnFeaturePicked事件中调用自定义 C 函数而非在蓝图中做 JSON 解析。CesiumForUnreal 对 3D Tiles 的加载不是“有文件就能用”而是要求数据在坐标系、二进制结构、语义字段三个维度上同时达标。从 shp 到可点击的三维建筑关键不在转换工具有多强大而在每一步是否守住transform矩阵的 ECEF 精度、batch table的BATCH_LENGTH一致性、以及tileset.json的层级衰减逻辑。真正决定项目成败的往往是ogr2ogr命令里一个-t_srs参数或是batched-tiles脚本中--batch-id-property的拼写。本文还有配套的精品资源点击获取