本篇的四个功能——Large World Rendering(浮点原点)、Geospatial Camera(地理空间相机)、3D Tiles、Physically Based Atmosphere(基于物理的大气)——服务于同一个场景域:数字地球、智慧城市、飞行模拟、太空游戏。它们的关系是:浮点原点是地基(让大坐标不抖)、地理相机是交互(让人能导航行星)、3D Tiles 是数据(让海量地理数据流进来)、大气是氛围(让世界看起来真实)。官方在发布稿中把这四个打包为"地理空间能力"整体推出,本篇也按这个逻辑一次讲透。
一、Large World Rendering:浮点原点,大世界的地基
1.1 问题:32 位浮点的物理极限
GPU 顶点计算用的是 32 位浮点数,尾数只有 24 位(2²⁴ ≈ 1677 万)。这意味着:当坐标量级达到 10⁷(一千万)时,浮点数的最小步进已经接近1 个单位——顶点位置无法精确表示,只能"跳格子"。症状就是大坐标场景的经典三连:mesh 抖动、阴影闪烁、动画卡顿。
这不是 Babylon 独有的问题,而是所有 3D 引擎的宿命。Babylon 论坛上这类求助帖攒了将近十年——"我想在 (6738120, 1384, -4123270) 显示一个立方体,为什么变形了"、"10 万米外的渲染全是问题"、"相机平移时模型丢失精度"……官方在发布公告里一口气 @ 了十几个历史帖子,可见怨气之深。
Floating Origin 效果对比:关闭时大坐标下模型抖动破碎(左),开启后渲染平滑稳定(右)
1.2 原理:相机不动,世界动
解法是图形学里的经典技巧floating origin(浮点原点,Chris Thorne 首次系统描述),思想一句话:让相机永远"待"在世界原点,改为移动所有物体。
相机当然还能动,只是动法变了:用一个双精度的 Vector3 记录相机的真实位置,渲染前把"物体双精度坐标 − 相机双精度坐标"的差值设给物体。举例:小行星在 (10000000, 0, 10000000),相机在它旁边 500 单位处。偏移之后,小行星相对相机的位置是 (0, 0, −500)——传给 GPU 的永远是小数值,精度问题消失;而远处真正巨大的坐标反正看不见,抖了也无所谓。
1.3 Babylon 的实现:在最后一刻拦截
Babylon 9.0 的实现分两条腿:
useHighPrecisionMatrices(引擎级):CPU 侧所有矩阵计算强制使用64 位双精度,保证中间数学不丢精度;floatingOriginMode(场景级):在矩阵 uniform 传给 shader 的最后一刻做偏移——world的平移减去相机位置、view的平移清零、worldViewProjection先分解再分别偏移再重组;连带偏移的还有视线位置(vEyePosition,保证光照和反射正确)和裁剪面(d' = d + normal·offset)。
实现手段是 monkey-patchEffect.setMatrix和UniformBuffer._updateMatrixForUniform,按 uniform 名字拦截处理——Node Material 自定义块的u_前缀 uniform 也被覆盖。
最值得称道的是设计哲学:偏移只在传给 shader 前发生,所有公开的矩阵和位置 getter 完全无感——camera.position读到的依然是 (1e11, 0, 0),你的业务逻辑、Babylon 内部逻辑,一行都不用改。
1.4 用法:一行开启
// 方式一:引擎级,所有场景自动启用(推荐) const engine = new BABYLON.Engine(canvas, true, { useLargeWorldRendering: true, // = 高精度矩阵 + 全部场景 floating origin }); // 方式二:场景级(比如主世界开启、UI 场景不开) const engine = new BABYLON.Engine(canvas, true, { useHighPrecisionMatrix: true, // 引擎级仍必须开,它是全局设置 }); const worldScene = new BABYLON.Scene(engine, { useFloatingOrigin: true }); const uiScene = new BABYLON.Scene(engine); // 不受影响场景级控制的意义是性能:小坐标场景可以省掉偏移计算的开销。
哪些子系统开箱即用?官方清单很长:Standard/PBR 材质、Node Material、阴影(含 CSM 级联阴影)、CPU/GPU 粒子、精灵、各类灯光(含集群光照)、包围盒/边线渲染器、背景/天空/水材质、大气 addon、gizmo 等 utility layers。唯一的注意事项:自定义 shader 如果用了位置相关 uniform,务必使用标准命名(world、view、viewProjection、worldViewProjection),否则拦截器认不出来。
1.5 加分项:Havok 多区域物理
渲染解决了,物理呢?Havok 同样吃不下大坐标。9.0 的答案是多区域架构:物理世界被划分成多个区域,每个区域有自己的浮点原点,刚体永远在自己区域的原点附近模拟:
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance, { floatingOriginWorldRadius: 100000, // 每个物理区域的半径,默认 10 万 }); scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), havokPlugin);区域管理机制相当完善:刚体创建时自动归入包含它的区域(没有就新建);跨区域迁移带 20% 滞后边界防抖;离开区域时按速度前瞻一秒选目标区域,避免高速物体产生无谓的中间区域;空区域自动回收;甚至每个区域可以有独立重力向量——多行星不同引力场的场景直接支持。半径选择是权衡:大了区域少但远端精度略降,小了精度高但管理开销大,默认 10 万对多数应用是甜点。
二、Geospatial Camera:为行星而生的相机
Geospatial Camera:为环绕球形行星设计的相机,开箱即用的地图式交互
有了不抖的世界,还需要匹配的导航方式。ArcRotateCamera围绕一个点转,而地理空间场景要的是围绕一颗球转。Geospatial Camera 专为"球心在世界原点的行星"设计,开箱即用的地图式交互:
拖拽平移:globe-anchored——指针"抓住"球面拖动,和 Google Earth 手感一致
滚轮缩放:朝光标位置缩放,缩放量随轨道半径自适应
右键倾斜、键盘与触摸支持、可配置的限制与惯性
flyToAsync:平滑的动画飞行(支持直线和弧线两种插值)碰撞检测、按海拔自动调整远近裁剪面、极点钳制
最后一点特别值得展开:从轨道高度(需要极远的 far plane)一路飞到街景(需要极近的 near plane),裁剪面如果固定,深度精度必然崩。相机按海拔自动调整 clip plane,配合浮点原点,才构成完整的"从太空到街道"体验。
三、3D Tiles:海量地理数据的流式入口
3D Tiles:Babylon.js 流式渲染 Google Photorealistic 3D Tiles(纽约中央公园)
3D Tiles 是 Cesium 创建、OGC 采纳的开放标准,专为流式传输海量异构地理空间数据设计。Babylon 的选择很务实:不重复造轮子,官方推荐集成 NASA/AMMOS 的 3DTilesRendererJS(感谢 Garrett Johnson 的工作),它负责 tileset 遍历、LOD 选择和 tile 加载这些最复杂的部分:
npm install 3d-tiles-renderer @babylonjs/core接入有四个硬性要点:
// 1. 必须右手坐标系 scene.useRightHandedSystem = true; // 2. 全球尺度数据集(如 Google Photorealistic 3D Tiles)必须开浮点原点 const engine = new BABYLON.Engine(canvas, true, { useLargeWorldRendering: true }); // 3. 用 GeospatialCamera 做球面导航(小数据集用 ArcRotateCamera 也行) // 4. 每帧驱动 tile 更新 scene.onBeforeRenderObservable.add(() => tiles.update());Google Photorealistic 3D Tiles(谷歌地球的实景三维数据)有现成的官方示例——意味着你可以用 Babylon 渲一个可导航的真实地球。
这里值得说一句定位问题:这不是要取代 Cesium。官方与 Cesium 团队保持密切联系,纯 GIS 重度应用(坐标系转换、GeoJSON、完整测绘工具链)Cesium 依然是首选;Babylon 的优势在于把地理数据当成 3D 场景的一种数据源——和城市级 splat、BIM 模型、物理模拟、游戏逻辑混合。社区里已经有人用"Cesium 出 tiles、Babylon 管渲染和物理"的单 canvas 组合方案跑通了项目。
四、Physically Based Atmosphere:给世界一片天空
Physically Based Atmosphere:Rayleigh / Mie 散射模型生成的真实大气透视与远山层次
最后一个拼图是氛围。9.0 的大气 addon 是轻量 opt-in 包,基于物理精确的Rayleigh 散射 + Mie 散射 + 臭氧吸收 + 多次散射模型:
npm install @babylonjs/addonsimport { Atmosphere } from "@babylonjs/addons/atmosphere";它能给你:真实的日出日落和昼夜循环、高空航拍视角的大气透视(aerial perspective)、乃至轨道视角看地球的弧线光晕。与 PBR 材质和平行光无缝集成,散射参数全部可自定义——想给外星世界配一个紫色天空,改几个参数的事。
五、四位一体:数字地球的最小骨架
把四者拼起来的典型架构:
const engine = new BABYLON.Engine(canvas, true, { useLargeWorldRendering: true }); const scene = new BABYLON.Scene(engine); scene.useRightHandedSystem = true; // 相机:行星轨道导航 const camera = new BABYLON.GeospatialCamera("geoCam", /* planetRadius, center, */ scene); camera.attachControl(canvas, true); // 数据:3D Tiles 流式加载 const tiles = new TilesRenderer(tilesetUrl); scene.onBeforeRenderObservable.add(() => tiles.update()); // 氛围:物理大气 + 平行光(太阳) const atmosphere = new Atmosphere("atmosphere", scene); const sun = new BABYLON.DirectionalLight("sun", new BABYLON.Vector3(-1, -1, -1), scene); // 可选:Havok 多区域物理,让地面上能跑载具浮点原点保证从轨道到街景全程不抖,地理相机给交互,3D Tiles 喂数据,大气给天空——四者互相依赖、缺一不可,这就是为什么官方把它们作为一个整体发布。
六、小结与下篇预告
大世界四件套标志着 Babylon 正式进军地理空间领域:浮点原点用"最后一刻拦截"的优雅设计解决了十年老问题(业务代码零改动),多区域物理把 Havok 也带进了大世界,地理相机 + 3D Tiles + 物理大气则组成了完整的数字地球方案。建筑可视化、智慧城市、飞行模拟、太空游戏——这些以前需要 Cesium 或自研引擎的场景,现在 Babylon 原生可达。
下一篇是本系列的收官:材质、渲染细节与工具链——OpenPBR 材质标准、SDF 文本、Outline Renderer、Dynamic IBL Shadows、Nav Mesh 与音频更新、Inspector v2、Viewer 与 3MF 导出。