@lijuhong1981/modelto3dtiles
v2.1.1
Published
将obj/fbx/gltf/glb精细模型转换为3D Tiles的命令行工具,面向Cesium加载,支持空间切分、纹理图集、Draco压缩与构件级拾取
Maintainers
Readme
modelTo3DTiles
将普通精细模型转换为 3D Tiles 的命令行工具,可在 Cesium 或 threejs 中加载,支持 .obj、.fbx、.gltf、.glb 格式。
特性
- 多格式输入:obj(含 mtl/贴图)、fbx(自动读取文件声明的 UpAxis)、gltf/glb(含 Draco 压缩输入)
- 坐标自动归一化:向上轴自动转换为 glTF 规范的 Y-up,锚点自动修正至模型包围盒中心,支持经纬度定位与自动贴地
- 空间切分:瓦片按空间递归切分聚集,视锥剔除按瓦片生效
- 纹理图集:可合并材质的贴图合成 2 的幂图集页并重映射 UV,显著减少 draw call
- 贴图外置去重:瓦片间共享贴图文件(按内容哈希),避免重复下载与显存占用
- Draco 几何压缩:瓦片几何体积压缩约 90%,加载端由 Cesium 自动解码
- 实例化保留:glTF 输入中共享几何的实例构件(如族实例)以
EXT_mesh_gpu_instancing+EXT_instance_features输出,只存一份几何 + 每实例矩阵表,体积与转换内存大幅下降,逐构件拾取/显隐不受影响(需 CesiumJS ≥ 1.107) - BIM 语义属性:配合 revitToGltf 插件导出的
.metadatasidecar(--md),构件的名称/元素ID/类别/族/类型/楼层/参数集写入EXT_structural_metadata多列属性表,支持按构件查询与按楼层/类别过滤 - 构件级拾取:输出附带
EXT_mesh_features/EXT_structural_metadata扩展,Cesium 中可按构件(如单个座椅)拾取、查询、显隐、着色;同一构件的多个图元归并为单一 featureId - b3dm / glb 双格式:默认 b3dm(3D Tiles 经典格式),可切换 glb 瓦片(3D Tiles 1.1)
安装
npm 安装(需 Node.js ≥ 18):
# 全局安装后使用 modelTo3DTiles 命令
npm install -g @lijuhong1981/modelto3dtiles
# 或免安装直接运行
npx @lijuhong1981/modelto3dtiles -i ./model.objexe 免安装(Windows):从 Releases 下载 modelTo3DTiles.exe。
从源码运行:
git clone https://github.com/lijuhong1981/modelTo3DTiles.git
cd modelTo3DTiles
npm install
npm run build:esbuild使用
npm 全局安装或 exe 方式下,直接使用 modelTo3DTiles 命令:
# 基本转换(obj/fbx/gltf/glb 均可),默认已启用纹理图集与Draco压缩
modelTo3DTiles -i ./model.obj
# BIM模型:连同revitToGltf导出的.metadata一起转换,构件属性随瓦片写入属性表
modelTo3DTiles -i ./station.glb --md ./station.metadata
# 指定输出目录与经纬度(默认 116.4074,39.9042)
modelTo3DTiles -i ./model.fbx -o ./output --lla 106.55,29.56,0
# 追求最高画质、加载端零解码开销:关闭Draco压缩与纹理图集
modelTo3DTiles -i ./model.gltf -o ./output --no-draco --no-ta从源码运行时,将命令换为 node main.js,参数完全相同:
node main.js -i ./model.obj输入模型向上坐标轴自动识别与转换:obj 默认按 Z-up 处理(可用 --inputUpAxis 指定),fbx 读取文件 GlobalSettings 声明的 UpAxis,gltf/glb 按规范固定 Y-up。
命令参数
|参数名称|别名|描述|类型|默认值|是否必须| |:---:|:---:|:---:|:---:|:---:|:---:| |--help|-h|显示帮助|Boolean||否| |--version|-v|显示版本号|Boolean||否| |--input|-i|输入模型路径|String||是| |--output|-o|模型输出目录,不填则在模型文件目录下自动创建一个3dtiles目录。|String||否| |--metadata|--md|输入BIM语义sidecar(.metadata),将构件的BIM属性(名称/元素ID/类别/族/类型/楼层/参数集)写入瓦片内EXT_structural_metadata多列属性表,支持加载端按构件查询与按楼层/类别过滤。.metadata由revitToGltf插件导出,其Elements数组的Key字段与glTF节点extras.uniqueId一一对应。|String||否| |--inputUpAxis|--iua|设置输入模型的向上坐标轴,[可选值:X,Y,Z,-X,-Y,-Z]obj默认按Z-up处理并自动转换,fbx默认读取文件GlobalSettings声明的UpAxis,gltf/glb按规范固定为Y-up。|String|见描述|否| |--rotation|-r|手动设置模型旋转角度,格式为[x,y,z],单位为度。坐标轴朝向已自动归一化,仅当模型存在轴向以外的偏转(如偏离正北)时才需要设置。|String||否| |--lngLatAlt|--lla|设置模型经度、纬度、海拔高度,格式为[longitude,latitude,altitude],高度单位为米。|String|116.4074,39.9042,0|否| |--correctCenter|--cc|自动修正模型锚点至模型包围盒中心点,修正后经纬度对应包围盒中心。|Boolean|true|否| |--b3dm||以b3dm容器输出瓦片,3D Tiles经典格式,兼容传统前端;关闭则输出glTF瓦片(3D Tiles 1.1)。|Boolean|true|否| |--split|-s|设置瓦片拆分方式,[可选值:spatial,material]material按材质顺序装填(默认,适合单精度精细模型,draw call恒为材质数、体积最小、加载最快);spatial按空间递归切分并生成LOD层级瓦片,远景渲染粗层、近景refine细层,适合大体量场景的漫游剔除,体积约为material的4倍,同精度模型下收益有限。|String|material|否| |--textureAtlas|--ta|启用纹理图集优化:将可合并材质的贴图按尺寸分桶合成2的幂网格图集页并重映射UV,减少材质数与draw call;非2的幂贴图重采样至最近2的幂。仅baseColor贴图、UV在[0,1]内的材质参与合并。|Boolean|true|否| |--resampleTextures|-rst|将非2的幂贴图重采样至最近2的幂(含全部贴图槽位),避免Cesium将NPOT纹理放大到下一2次幂导致显存膨胀;启用textureAtlas时无需单独开启。|Boolean|true|否| |--draco|-d|启用Draco几何压缩(KHR_draco_mesh_compression),瓦片体积大幅下降,加载端由Cesium自动解码;含featureId属性,与构件拾取兼容。|Boolean|true|否| |--mergePrimitive|-mp|设置是否合并材质相同的网格图元。|Boolean|true|否| |--instancing||保留glTF输入的实例化网格(EXT_mesh_gpu_instancing+EXT_instance_features):同几何多构件(如族实例)只存一份几何+每实例矩阵表,体积与转换内存大幅下降,构件拾取/显隐不受影响;仅material拆分模式生效,spatial自动回退为展开。需Cesium 1.107+加载。|Boolean|true|否| |--tileSize|-ts|设置期望的单个瓦片存储容量,单位mb。|Number|10|否| |--clampToGround|--ctg|设置模型是否自动贴地,为true时altitude属性失效。|Boolean|true|否| |--noneTransform|--nt|是否不设置模型变换矩阵,为true时lngLatAlt、clampToGround等属性失效。|Boolean|false|否|
另有面向 obj 材质转换的高级参数(透传自 obj2gltf,一般无需设置):--checkTransparency、--packOcclusion、--metallicRoughness、--specularGlossiness、--unlit,详见 modelTo3DTiles -h。
调参建议
瓦片策略(--split)的选择取决于模型精度结构与浏览方式:
| 场景 | 推荐配置 | 原因 |
|---|---|---|
| 单精度精细模型(默认情况) | -s material(默认) | draw call 恒为材质数(理论下限),体积最小、加载最快、行为可预期 |
| 大体量场景 + 漫游剔除 | -s spatial | 生成LOD层级瓦片:远景渲染粗层(根瓦片图元数等于材质切分下限),镜头推进逐分支refine近景细层 |
说明:当前粗层与细层为同一精度的几何(仅聚合粒度不同),spatial 模式体积约为 material 的 4 倍,近景 draw call 略高于 material——它的完整收益依赖多精度简化(粗层真减三角形),适合确认需要漫游剔除的大场景再启用;配合 --ts 2~4 调细叶子粒度可增强剔除敏感性。
输出结构
output/
├── tileset.json # 3D Tiles 入口
├── Tile-0.b3dm # 瓦片内容(b3dm或glb)
├── Tile-1.b3dm
└── textures/ # 外置共享贴图与图集页注意:textures/ 目录与瓦片文件必须整体分发,不可只拷贝 .b3dm 文件。
Cesium 加载
const tileset = await Cesium.Cesium3DTileset.fromUrl('output/tileset.json');
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
// 构件级拾取:点击查询构件名称、单独显隐/着色
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(movement => {
const feature = viewer.scene.pick(movement.position);
if (feature instanceof Cesium.Cesium3DTileFeature) {
console.log(feature.getProperty('name')); // 构件名称
feature.color = Cesium.Color.RED; // 单构件高亮
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);版本要求:CesiumJS ≥ 1.104(Cesium3DTileset.fromUrl API);构件级拾取(EXT_mesh_features)需 ≥ 1.97,实例化输出(EXT_mesh_gpu_instancing/EXT_instance_features)需 ≥ 1.107(实测 1.137 可用),建议直接使用最新版。
在 threejs 中使用
两种方式,按场景选择。
方式一:直接加载瓦片 GLB(简单场景,自行管理相机与剔除)
转换时用 --no-b3dm 输出 glTF 瓦片,遍历 tileset.json 逐个加载合成。输出为模型局部坐标系(米,Y-up),不含地理定位,需自行摆放:
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
// 默认开启的Draco压缩需要配置解码器
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/');
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
const group = new THREE.Group();
const tileset = await (await fetch('output/tileset.json')).json();
for (const child of tileset.root.children) {
const gltf = await loader.loadAsync('output/' + child.content.uri);
group.add(gltf.scene);
}
scene.add(group);方式二:经 3d-tiles-renderer 加载完整 tileset(需要瓦片调度、视锥剔除时)
import { TilesRenderer } from '3d-tiles-renderer';
const tiles = new TilesRenderer('output/tileset.json');
tiles.setCamera(camera);
tiles.setResolutionFromRenderer(renderer);
scene.add(tiles.group);
// 渲染循环中更新
tiles.update();说明:
- 启用
--draco时务必配置DRACOLoader(b3dm 容器需自行剥离 28 字节头再交由 GLTFLoader,建议直接用--no-b3dm); textures/目录需与瓦片同目录部署(外置贴图按相对路径解析);- 构件拾取:GLTFLoader 不识别
EXT_mesh_features/EXT_instance_features扩展,但_FEATURE_ID_0属性会保留在几何体上——实例化构件(EXT_mesh_gpu_instancing)被渲染为InstancedMesh,拾取用intersection.instanceId读实例属性geometry.getAttribute('_FEATURE_ID_0')(loader 原样保留实例语义大小写);合并网格为逐顶点属性,但 loader 会把自定义顶点语义小写化为_feature_id_0,拾取用intersection.face.a顶点索引。构件名称/属性可自行解码 GLB JSON 块中EXT_structural_metadata的属性表(stringOffsets 均为 BIN 块内偏移)。
从源码构建 exe
npm install
npm run build # esbuild打包 + pkg封装为 node22-win-x64 可执行文件产物约 210MB(内嵌 Node 运行时、Cesium 资源与 Draco/图像原生库),首次构建需从网络下载 Node 运行时。
