多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

3D Tiles是什么?从在线打开到数字孪生与元宇宙场景落地

3D Tiles是什么?从在线打开到数字孪生与元宇宙场景落地 “先把tileset.json给我我看一眼。”——这是我在做城市级数字孪生项目时几乎每天都得说的一句话。那个项目里有几百平方公里的倾斜摄影模型、BIM体量和激光点云最初大家把原始osgb文件拷来拷去一个项目组几个人光同步数据就能耗掉半天。后来我们把所有数据统一转成3D Tiles放进浏览器里交给CesiumJS去跑整个团队的协作方式彻底变了。3D Tiles到底是什么它和地理空间、元宇宙又有什么具体关系最关键的是普通开发者拿到一份3D Tiles数据怎么才能在线打开、立刻看到效果这篇文章我就按自己的实操路径来聊尽量不端着讲理论给你能直接照着做的东西。适合刚接触三维GIS、数字孪生、WebGL可视化或者想把手里的倾斜摄影/点云/BIM数据快速搬到网页和元宇宙场景里的朋友。1. 3D Tiles究竟是什么1.1 从一次渲染崩溃说起好几年前我第一次在浏览器里加载一栋楼的精模用的还是最原始的glTF格式。模型本身不算大但加上几十个楼盘、几千棵树浏览器直接卡死风扇起飞页面白屏。问题的根源不是电脑配置差而是数据组织和传输策略不对——我们试图一次把整个场景的全部三角形、纹理都塞给GPU。后来我理解到3D Tiles本质上就是为解决这件事出现的。它不是一个单文件而是一种“瓦片化 层级细节”的三维空间数据规范由Cesium团队提出2016年开放2019年左右逐步成为社区广泛使用的标准。它的思路和地图金字塔切片很像——你在手机上划电子地图时绝不是一次下载全国所有瓦片而是只下载当前视野内需要的、清晰度合适的那些瓦片缩放或移动时再继续加载。3D Tiles把这种思路从二维地图延伸到了三维空间。你可以把3D Tiles想象成一套“三维快递系统”每个街区、每栋楼、每根管线都被切成许多大大小小的包裹浏览器只拆开眼前需要的包裹看完就丢靠近了再拆更细的。这里面有个关键概念叫“瓦片集”入口是tileset.json它像一张快递目录记录着整个三维场景的空间范围、层级关系、每个瓦片的包围盒和内容地址。1.2 它不是一种文件格式很多新手会问3D Tiles是不是类似OBJ、FBX那种文件格式不是。准确说它是一套“容器与组织规范”里面可以装多种内容格式常见的包括瓦片内容类型典型适用场景b3dmBatched 3D Model倾斜摄影模型、批量建筑体一个瓦片几百个模型i3dmInstanced 3D Model大量重复物体树、路灯、电线杆、风力发电机pntsPoint Cloud激光雷达点云、百万级点量场景cmptComposite多种内容混合打包在一个瓦片里vctrVector三维矢量数据例如建筑白模、道路边线这种设计的好处是灵活。一棵树的实例可能只需要一份模型位置信息用矩阵记录一万棵树也不会让你下载一万份模型一片小区的倾斜摄影则会被切成几十个b3dm瓦片每个瓦片包含一批 mesh 和纹理。另外每个瓦片还能带属性信息比如建筑的楼层、权属、年份这些存储在Batch Table批量属性表里。这就让3D Tiles不仅仅是“看起来好看”的三维模型还能作为空间分析的数据底座。这也是它能被用于数字孪生、园区管理、灾害模拟的直接原因。1.3 它凭什么能成为“地理空间到元宇宙”的桥梁元宇宙这个词这两年热度很高但不管元宇宙最终形态如何“真实世界的空间底图”一定是标配。你进入一个虚拟园区、虚拟城市总不能一片虚空吧总得把真实地形、建筑、道路、室内结构装进去。这种需求落到技术上就需要一个能承载数十GB甚至TB级地理空间数据、支持按需流式加载、能在浏览器和游戏引擎里通用、并且带坐标和属性的开放标准。3D Tiles恰好满足这些条件。第一流式加载让它能跑在海量数据上第二开放规范意味着不绑定某个商业软件第三它基于glTF体系与目前主流的WebGL/WebGPU、游戏引擎生态天然兼容。实践里我用Cesium for Unreal直接在虚幻引擎里流式加载过某园区的3D Tiles效果和一个完整场景资源包几乎一致但不用预先下载全部数据。这已经非常接近“把真实地理空间流传到元宇宙”的工作方式了。提示3D Tiles和glTF的关系可以理解为“glTF是单个3D对象的压缩包3D Tiles是整个地球的快递分拣系统”。两者配合一个管单件一个管海量调度。2. 为什么元宇宙需要3D Tiles这种“空间快递系统”2.1 元宇宙的底层不是VR眼镜而是数据组织方式聊元宇宙相关项目时很多朋友第一反应是“要做什么设备”“用什么引擎”。但我个人看下来真正决定项目成不成型的往往是数据怎么组织、怎么流通。想象一下你做一个虚拟旅游场景需要把某个景区的真实地形、建筑、植被都放进去。如果数据组织方式是“把整个景区模型打包成一个几十GB的FBX”那场景加载时长会劝退所有用户。如果用3D Tiles做组织系统就知道先加载游客视野最近的几栋建筑再根据摄像头视角动态加载远处山体的粗略mesh。这不是偷懒而是符合现实世界感知逻辑——人本来就只能看清眼前的东西。这也是“地理空间流传到元宇宙”这句话的技术本质。地理空间数据通常体量巨大、坐标精确、类型复杂而元宇宙需要的是持续运行、多人并发、实时交互。3D Tiles把前者“翻译”成后者能消化的形态我甚至觉得目前能在这两者中间充当通用交换格式的角色3D Tiles是最成熟的之一。2.2 数字孪生和元宇宙为什么都拿它当底座数字孪生偏“实”元宇宙偏“虚”但两者都需要一个能把真实世界搬进计算机的管道。这几年我接触到的数字孪生项目几乎没有一个不用到倾斜摄影和3D Tiles的。比如做一个化工园区的安全应急系统你需要把全园区的储罐、管廊、建筑用倾斜摄影重建出来同时把每根管道的材质、压力、检修记录挂到对应的三维对象上。这个需求用3D Tiles的Batch Table可以比较方便地实现每个储罐是一个i3dm实例属性表里存罐号、容积、最近检测日期前端点击模型弹出属性面板后台还能根据属性做预警联动。放到元宇宙语境里也一样你在虚拟空间里看到的“数字城市”底层如果是3D Tiles那它就不只是视觉模型而是和地理坐标对齐、具备语义信息、可以查询和模拟的“活”空间。2.3 跨引擎工作流同一个3D Tiles多种打开方式在线打开3D Tiles还有一个很实际的好处它能在不同引擎之间流动而不需要重复建模。我用同一份某大型商业综合体的3D Tiles数据分别试过CesiumJS的Web端、Cesium for Unreal的实时场景以及通过Blender插件导入后调整材质。三套流程打开的是同一份数据但表现方式完全不同——Web端偏信息展示UE端偏沉浸漫游。这个工作流的成本重心完全在“数据生产”和“数据组织”阶段一旦转成3D Tiles后面所有消费端都受益。3. 如何在线打开3D Tiles实操方法全记录3.1 最快的路径用在线预览工具直接拖文件如果你手头没有代码环境只想快速看效果我建议用现成的在线预览工具。最省事的路径是注册一个Cesium ion账号把数据类型为“3D Tiles”的文件或原始数据例如OSGB、LAS点云、OBJ拖上去平台会自动帮你处理转换然后给你一个预览链接和一个Token。实际操作登录ion后点“Add assets”把本地文件压缩包传上去等进度条跑完系统会生成一个Asset。点击该Asset详情页里面会出现一个“Preview”按钮点开就能在浏览器里旋转、测量、看属性。同时你会拿到一个Access Token和一个Ion Asset ID把这两样写进CesiumJS代码就能在自己网页里加载这份数据。这个方案适合“快速验证数据质量”例如你看看倾斜摄影拍得全不全、有没有空洞、纹理是否清晰。缺点同样明显在线平台转换有队列等待数据量大时耗时很长而且数据上传外部平台涉及数据安全和隐私时要慎重。如果项目里有涉密或敏感数据我强烈不建议图省事走这个路径。3.2 完全开源路线本地起服务打开自己的tileset.json不依赖任何商业平台你也能在线打开3D Tiles。核心就是两件事把tileset.json和一个Web服务搭起来再用CesiumJS去加载。第一步确认你的数据已经是3D Tiles结构。展开文件目录你应该能看到一个tileset.json后面跟着一堆嵌套的瓦片文件结构类似下面这样YourTiles/ ├── tileset.json ├── tiles/ │ ├── 0.b3dm │ ├── 1.b3dm │ └── ...如果没有tileset.json光有b3dm也没用那只是零散瓦片不是完整瓦片集。实际转换工具会生成规范的结构你也可以用Cesium ion导出一个“3D Tiles”格式的包或使用开源转换工具从OBJ/OSGB生成。第二步起一个本地HTTP服务。很多人第一步就踩坑直接双击html文件用file://协议打开页面结果浏览器因为跨域问题CORS不加载模型控制台一片红色报错。正确的做法是在项目目录下开一个静态服务。如果你装了Python进入项目根目录执行python -m http.server 8080然后在浏览器打开http://localhost:8080就能正常请求本地数据文件。如果你用的是前端开发环境比如Vite、Webpack也可以直接把数据放进public目录由开发服务器托管。第三步新建一个HTML文件引入CesiumJS并加载tileset。下面这段代码我保留了一个最小化可运行版本!DOCTYPE html html langzh-CN head meta charsetutf-8 title在线打开3D Tiles/title style html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } /style !-- 可以换用本地构建的Cesium npm包这里只是快速示例 -- script srchttps://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Cesium.js/script link hrefhttps://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Widgets/widgets.css relstylesheet /head body div idcesiumContainer/div script // 如果你用的数据不是Cesium ion托管的可以不填token但如果用了ion服务就必须填 // Cesium.Ion.defaultAccessToken 你的token; const viewer new Cesium.Viewer(cesiumContainer, { baseLayerPicker: false, animation: false, timeline: false, geocoder: false }); // 这里的url可以是本地地址也可以是远程服务器上的tileset.json const tileset viewer.scene.primitives.add( new Cesium.Cesium3DTileset({ url: ./YourTiles/tileset.json }) ); viewer.zoomTo(tileset); /script /body /html这段代码里核心调用就是Cesium.Cesium3DTileset传入唯一的url参数指向tileset.json。Cesium拿到这个入口文件后会自动分析瓦片树根据视角动态请求子瓦片。viewer.zoomTo会把相机飞到数据包围盒范围内。重要提示tileset.json本身很小但它引用的b3dm/pnts等文件才是大头。所以在线打开时网页第一次请求的是tileset.json和当前视角可见的少数瓦片目测加载速度会很快后期随着镜头拉近数据请求会越来越频繁这是正常的LOD行为。3.3 把OSGB、点云、OBJ转成3D Tiles的取舍在线打开3D Tiles之前你多半需要把原始数据转成3D Tiles格式。我常遇到的原始数据类型有三种倾斜摄影模型常见格式为OSGB目录结构复杂Data/xxx/Tile_xxx/LOD_xxx/xxx.osgb一般由ContextCapture、Smart3D、大疆智图等软件产出点云常见格式为LAS/LAZ来源是机载或地面激光雷达手工模型或BIM导出模型常见格式为OBJ、FBX、IFC。转换选型方面我的经验是数据量小几十个模型物件直接用Cesium ion拖拽上传最省事数据量大且需要私有化就用开源工具或建模软件插件来转。开源生态里py3dtiles能直接在Python环境里把点云或简单模型转成3D Tilespg2b3dm能从PostGIS里把带三维的地物导出为b3dmCesium官方也有从OBJ转换的示例。无论哪个工具转换后一定要检查两件事坐标系统和空间范围。3D Tiles要求数据使用地理坐标或适合场景的局部坐标如果你的源数据是平面坐标而没有正确的坐标定义转换完成后很容易出现模型浮在半球背面、位置偏移等问题。很多转换工具会让你设置坐标基准和偏移量不要跳过。我之前拿到一份没有坐标信息的OBJ时转换后数据默认落在经纬度0,0附近加载出来在海上排查半天才发现是源数据缺了参考坐标。3.4 在线打开时的参数调优屏幕上误差和细节级别在线打开3D Tiles默认情况下就能看到不错的效果。但如果你希望它在不同机器上更流畅或更清晰需要知道两个关键参数。第一个叫最大屏幕空间误差maximumScreenSpaceError默认值通常是16。它的含义是“允许屏幕上几何误差的最大像素数”。数值越大系统越倾向于加载粗糙层级瓦片画面相对模糊但性能好数值越小越倾向于加载精细瓦片模型更精细但请求更多、帧率下降。我的常用实践经验普通Web展示设为16足够如果需要近距离查看结构细节可以把它临时调到8或4看完再还原否则帧数会很难看。第二个是瓦片缓存限制tileset.maximumMemoryUsage默认值是512MB左右。如果数据覆盖范围很大且你不停转视角内存占用容易飙升。把它设成一个更小的值例如256Cesium会按策略淘汰缓存瓦片。实际测试中这能明显缓解大场景带来的卡顿。const tileset new Cesium.Cesium3DTileset({ url: ./YourTiles/tileset.json, maximumScreenSpaceError: 16, maximumMemoryUsage: 256 });3.5 在Unreal和Unity里“在线打开”3D Tiles如果要搭建更接近“元宇宙”的沉浸体验Web浏览器只是第一步很多人最终想把3D Tiles接进游戏引擎。Cesium官方维护了Cesium for Unreal和Cesium for Unity两套插件。以Cesium for Unreal为例安装好插件后新建一个Cesium3DTileset节点填入tileset.json地址或Cesium ion Asset ID它就会利用引擎的流式加载机制将瓦片拉进关卡再配合CesiumGeoreference节点处理坐标基准。Unreal侧会自动生成LOD层级视觉避开了粗糙加载的“弹跳感”在Sequencer里做漫游镜头也相对顺滑。Unity侧的工作流类似安装Cesium for Unity包场景里创建一个Tileset对象并指定数据源。需要注意游戏引擎里通常没有浏览器那种基于帧预算的动态调度所以如果你的场景数据量极大最好在数据生产阶段就做分层优化而不是指望引擎硬扛。4. 在线打开3D Tiles的常见问题排错实录4.1 高频问题速查表现象可能原因快速排查方向页面白屏无任何模型tileset.json路径错误或跨域被拦F12看Network查找tileset.json请求是否404模型闪烁/跳变像在抖LOD切换过于频繁调大maximumScreenSpaceError检查瓦片包围盒是否正确加载缓慢数据一直转圈没有启用gzip压缩、瓦片粒度太粗给服务器加压缩重新做瓦片层级模型位置跑到海里坐标系统定义错误检查数据投影确认使用EPSG:4978或正确ENU基准点开模型属性为空数据本身没有Batch Table检查转换时是否勾选“保留属性”或手工挂接属性表帧率低到没法用单帧请求瓦片过多GPU饱和限制最大内存、减小SSE、降低相机FOV用了本地文件双击打不开浏览器的file://跨域限制用python -m http.server或Nginx托管数据目录4.2 白屏问题最详细的排查方法我调试3D Tiles加载时无论有多少年经验第一件事永远是打开浏览器开发者工具切到Network面板刷新页面盯着查找name里包含tileset或b3dm的请求。如果tileset.json请求都没有说明你的URL路径写错了或者服务器目录没配对。如果你用的是Cesium ion要确认代码里已经设置了Cesium.Ion.defaultAccessToken否则ion会拒绝请求返回401。如果tileset.json请求成功但后续b3dm请求全部失败多半是服务器CORS没有配置。跨域问题是本地调试的经典坑解决方法是给静态服务加上响应头或直接用Nginx反代。Nginx示例片段location /YourTiles/ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, OPTIONS; add_header Access-Control-Allow-Headers Content-Type; }这里允许所有来源是为了调试方便生产环境里要收紧到你的站点域名。还有一个容易忽略的问题如果你把数据放进对象存储比如阿里云OSS、腾讯云COS、AWS S3记得在存储桶的CORS规则里显式允许来自Web端的请求否则浏览器也会拦截。4.3 坐标偏移问题为什么模型总是不在预期位置以我接触过的项目来看坐标偏移是仅次于白屏的第二大坑。3D Tiles加载时Cesium默认把瓦片集放在WGS84椭球体或使用局部ENU坐标。如果原始数据在定义时使用了不正确的坐标框架或者没有把原点偏移信息写进tileset.json加载出来的场景就会出现模型和地形分离、模型跑到地心、全部挤在原点附近等现象。排查思路先确认原始数据的坐标系。倾斜摄影软件通常会给出一个metadata.xml里面有坐标系和原点的经纬度坐标。转换时把这个信息正确填进去并且确保tileset.json的asset.gltfUpAxis和transform矩阵与源数据一致。如果你是手工构建tileset.json强烈建议用成熟工具生成不要自己手写矩阵——手写坐标变换矩阵的出错概率太高。4.4 性能问题为什么加载顺畅但越看越卡有时候刚打开页面很流畅漫游了五分钟越来越卡。这个现象多半是瓦片缓存累积导致的。Cesium默认会缓存最近加载过的瓦片方便你转回视角时快速复用但如果场景太大、缓存策略没有限制内存会不断膨胀最终拖垮GPU。我调优时会把maximumMemoryUsage从默认值调低同时结合viewer.scene.globe.tileCacheSize等参数一起调。注意这个值不是越小越好太小会导致转视角时频繁回源重新下载瓦片反而卡顿。具体数值按你的数据量来城市级倾斜模型建议256MB到512MB起步精密单体模型可以更高。另一个常见性能因素是你把maximumScreenSpaceError调得太低。有些网友听说1就是极度精细就把SSE调到2加载不到一分钟浏览器内存就爆了。我的建议是展示型项目SSE16分析型项目SSE8左右不要再低除非是纯单栋建筑的小场景。5. 在线打开只是开始数据进入元宇宙前的优化建议5.1 打开之前先给数据“减重”在线打开体验是否顺畅和加载器的效率关系不大关键在数据本身的生产质量。第一次拿到倾斜摄影模型直接转3D Tiles后加载卡成PPT后来才明白源数据屋顶扭曲、纹理重复率高、三角面冗余这些都会在瓦片加载时放大。如果条件允许在转换之前先做模型轻量化简化网格、压缩纹理、去除重复点和冗余三角面。点云数据则可以提前下采样。这比在Cesium里调任何参数都有效。我见过有些数据原始60GB轻量化加重构瓦片后只有20GB但视觉效果几乎没差别。5.2 做好细节分层一份优秀的3D Tiles数据在浏览器里打开时应该让人几乎感觉不到“在加载”。想要达到这种效果数据生产端就得有清晰的细粒度规划。我的做法是外部环境地形、周边建筑做低精度粗模重点区域做高精度细模中间用2到3个过渡层级衔接。Cesium的瓦片树会把每层数据交给对应视角不要让你的场景全图都是高精度那样在线加载的体验很难救回来。5.3 把属性挂接放在转换之前而不是之后3D Tiles支持属性但很多在线打开后才发现没有属性。这是因为转换时没有把源数据里的属性字段映射到Batch Table。如果是BIM数据转换前一定要把构件ID、类型、楼层等字段整理好保证转换工具能识别。这样做完之后你在Cesium里点击模型才能拿到楼层和构件信息为后续做元宇宙场景里的“点击交互”“搜索定位”提供数据基础。5.4 从3D Tiles到元宇宙场景的完整工作流我目前比较认可的一套流程是这样的原始数据采集倾斜摄影/BIM/点云→ 数据清洗与轻量化 → 坐标基准统一 → 转换为3D Tiles → 部署到静态服务器或云存储 → 在CesiumJS/Cesium for Unreal中加载 → 接入属性查询、场景交互、多用户同步逻辑。到了最后一步你已经不是在“展示三维模型”了而是在构建一个具有地理空间语义的元宇宙场景。多用户同步这部分3D Tiles本身不管可以在应用层做用一个WebSocket服务广播相机位置、模型拾取事件和属性更新这样访问同一个3D Tiles场景的多个用户就能在统一的地理空间里看到彼此、操作同一批建筑对象。这是我目前实践下来“地理空间数据进元宇宙”最务实的实现方式不需要等什么大平台底层现有标准完全够用。5.5 发布前的数据合规检查不能省数据能在线打开意味着别人也可能在线打开。发布之前务必确认数据来源的授权边界、敏感地物是否已经处理、是否包含不可公开的设施信息。这一点在涉及园区、厂区、城市级真实场景时尤其重要。不要因为技术能实现就忽略数据使用范围的约束。我通常在项目里会专门列一条“可发布数据清单”把所有瓦片集和属性字段逐一过一遍确认之后才部署到外网服务器。最后再分享一点实际体会我在这类项目上踩过不少坑但其中最值钱的一条经验是拿到任何三维数据第一件事不是导入引擎、不是调材质而是先把它转成3D Tiles并用浏览器打开一遍。这一步能暴露坐标错误、数据空洞、文件缺失、属性丢失等一堆问题而且是肉眼可见地暴露比看日志高效太多。有一次我拿到一份电厂的倾斜摄影数据当天就转成3D Tiles在线打开结果发现有两座冷却塔的顶部网格直接塌陷了。如果等它做完贴图、接进UE、布置完交互再检查那返工成本至少翻三倍。所以我现在特别推崇“在线打开优先”的工作习惯——把3D Tiles当作数据质量的试金石先让它在浏览器里跑起来再谈后面所有花活。如果你手头正好有一份不知道如何处理的GIS或三维数据不妨先走一遍本文第3.2节的流程起个本地服务写20行CesiumJS代码把tileset加载出来。看见画面出现在浏览器里的那一刻前面所有折腾都值了。
返回列表