OSGB倾斜摄影模型读取与解析笔记
一、简介
OSGB是倾斜摄影三维模型最常用的格式(ContextCapture/大疆智图输出),基于OpenSceneGraph(OSG)二进制存储。
OSGB文件通常采用LOD金字塔结构:根节点(LOD0)存储低精度概览,子节点存储更高精度的局部模型。
二、OpenSceneGraph 下载与安装
OpenSceneGraph是官方读写OSGB/OSGT的标准工具集,包含osgviewer/osgconv等核心工具。
1. 下载地址
官方地址:https://objexx.com/OpenSceneGraph.html
2. 版本选择
推荐下载:
- OpenSceneGraph-3.6.5-VC2022-64-Release
- 解压即用,无需安装
3. 环境配置(可选)
将bin目录加入系统PATH,方便全局调用:
D:\Applications\OpenSceneGraph-3.6.5-VC2022-64-Release\bin
三、OSG 核心命令使用(osgviewer / osgconv)
1. osgviewer —— 模型查看器
作用:直接打开OSGB/OSGT/OSG模型,支持漫游、缩放、LOD预览。
常用命令:
# 基础打开
osgviewer.exe model.osgb
# 带统计信息打开(显示FPS、三角面数)
osgviewer.exe --stats model.osgb
# 强制使用单线程(避免大模型卡顿)
osgviewer.exe --single-threaded model.osgb
操作快捷键:
| 按键/鼠标 | 功能 |
|---|---|
| 鼠标左键 | 旋转模型 |
| 鼠标中键(滚轮键) | 平移模型 |
| 鼠标滚轮 | 缩放模型 |
| 空格键 (Space) | 自动飞到模型位置(最常用) |
| H 键 | 显示所有快捷键帮助菜单 |
| S 键 | 开关帧率 / 模型统计信息(FPS、三角面数等) |
| ESC 键 | 退出查看器 |
| W 键 | 线框模式开关 |
2. osgconv —— 格式转换
作用:二进制OSGB ↔ 文本OSGT互转,查看内部节点结构。
常用命令:
# OSGB 转 可读文本OSGT(解析内部结构)
osgconv.exe block.osgb block.osgt
# 合并多个瓦片(不推荐用于金字塔模型,会丢失LOD)
osgconv.exe *.osgb output.osgb
# OSGT 转 OSGB(生成二进制索引)
osgconv.exe root.osgt root.osgb
# 查看模型信息(节点数、顶点数等)
osgconv.exe -i model.osgb
四、OSGT 文件结构详解
OSGT = OpenSceneGraph Text,是OSG模型的文本格式,可直接用记事本打开分析。
1. 标准文件头
#Ascii Scene
#Version 161
#Generator OpenSceneGraph 3.6.5
2. 核心节点类型
(1)osg::Group —— 组节点(根索引常用)
用于包含多个子瓦片,作为总入口Root节点:
osg::Group {
UniqueID 1
Children 5 {
...子节点...
}
}
(2)osg::PagedLOD —— 分页LOD节点
判断是否为金字塔根节点的关键标志:
osg::PagedLOD {
UniqueID 2
CenterMode USER_DEFINED_CENTER
UserCenter 2137.05 -7322.91 241.05 -1 【模型中心点(局部坐标)】
RangeList 2 { 【显示距离范围】
50000 1e+30
0 50000
}
DatabasePath ".\\Data\\" 【子瓦片相对路径】
RangeDataList 2 { 【子瓦片文件名列表】
""
"Tile_+005_+006_L0.osgb"
}
}
- 根节点通常本身不包含几何体(
Children 0或只有PagedLOD),而是通过RangeDataList引用子瓦片。 RangeList定义了每个子瓦片生效的视距范围:例如[0, 50000)显示第一个子瓦片,[50000, +∞)显示空(不加载)。
(3)osg::Geode —— 几何节点(实际模型+纹理)
包含顶点、贴图、材质:
osg::Geode {
Drawables 1 {
osg::Geometry {
...
}
}
}
五、如何查找LOD0瓦片(根节点)
在实际数据处理中,找到正确的根节点是加载、合并或转换OSGB数据的第一步。以下是几种实用的技巧:
技巧1:按文件名长度筛选(最常用)
许多导出软件(如大疆智图)采用文件名长度表示LOD层级的命名规则:
- 最短的文件名即为LOD0根节点。
- 例如:
3143415263404252.osgb(16位)是根节点,31434152634042524.osgb(17位)是下一级,以此类推。
PS E:\GISData\osgb\taiwan> D:\Applications\OpenSceneGraph-3.6.5-VC2022-64-Release\bin\osgviewer.exe .\Data\3143415263404252.osgb

快速查找命令(Windows PowerShell):
Get-ChildItem -Filter *.osgb | Sort-Object { $_.Name.Length } -Descending | Select-Object Name, Length
或使用Linux命令:
ls -1 *.osgb | awk '{print length, $0}' | sort -n | head -10
技巧2:通过PagedLOD节点判断
用osgconv将可疑文件转为.osgt,搜索osg::PagedLOD:
- 若该文件包含非空的
RangeDataList(即引用了其他.osgb文件),则它很可能是LOD0根节点。 - 若它本身被其他文件引用(出现在父级
RangeDataList中),则它是子节点。
示例命令:
osgconv.exe candidate.osgb candidate.osgt
findstr /i "PagedLOD RangeDataList" candidate.osgt
技巧3:视觉验证法
用osgviewer分别加载几个候选文件,观察加载后的场景:
- 加载根节点时,应显示完整区域(但纹理模糊,几何简化)。
- 加载子节点时,通常只显示局部小块(高精度)。
操作步骤:
osgviewer.exe candidate1.osgb # 按空格键自动居中对齐,观察覆盖范围
osgviewer.exe candidate2.osgb
技巧4:多根节点识别
如果最短文件名有多个不同数值(如3143415263404252.osgb、3143415263404253.osgb等),说明数据集被分成了多个独立的地块,每个文件都是其对应地块的根节点。此时需要将这些根节点组合成一个总场景(例如通过一个顶层Group或3D Tiles的tileset)。
六、重建根节点:合并分块,全局预览
在实际生产场景中,Smart3D 等软件导出的倾斜摄影模型往往被切割成多个独立的数据块。例如,香港旺角区域的 OSGB 数据目录结构如下:

每个子目录代表一个独立的地块,其内部包含与目录同名的 .osgb 根节点文件。使用 osgviewer 只能单独加载某一个地块,例如:
osgviewer ./Data/Mongkok/Tile_+002_+014/Tile_+002_+014.osgb
显示效果仅为局部区域,无法快速纵览全局。

解决方案:构建顶层索引文件
若要一次性查看整个旺角区域,需要创建一个顶层索引文件,将所有地块的根节点引用汇总。该索引文件本身不存储几何数据,而是通过 osg::ProxyNode 记录各个子文件的路径,加载时再由 OSG 按需调度。
实现思路
- 遍历输入目录下的所有子文件夹;
- 对于每个子文件夹,检查是否存在与文件夹同名的
.osgb文件; - 若存在,创建一个
osg::ProxyNode节点,将其文件名设置为该.osgb文件的路径; - 将所有
ProxyNode添加到一个osg::Group中; - 将 Group 写入输出
.osgb文件。
核心代码
bool buildTileIndex(const std::string &inputDirectory, const std::string &outputFile)
{
namespace fs = std::filesystem;
fs::path inputPath = fs::absolute(inputDirectory);
if (!fs::exists(inputPath) || !fs::is_directory(inputPath))
{
std::cerr << "Error: input directory does not exist: " << inputPath << std::endl;
return false;
}
fs::path outPath = fs::absolute(outputFile);
fs::create_directories(outPath.parent_path());
std::cout << "Scanning directory: " << inputPath << std::endl;
std::cout << "Output file: " << outPath << std::endl;
osg::ref_ptr<osg::Group> rootGroup = new osg::Group;
int loadedCount = 0;
int skippedCount = 0;
for (const auto &entry : fs::directory_iterator(inputPath))
{
if (!entry.is_directory())
continue;
std::string dirName = entry.path().filename().string();
fs::path nodeFile = entry.path() / (dirName + ".osgb");
if (!fs::exists(nodeFile))
{
std::cerr << "Warning: missing root file " << nodeFile << std::endl;
skippedCount++;
continue;
}
// 创建代理节点,引用外部文件(无需实际读取几何数据)
osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
proxy->setFileName(0, nodeFile.string());
rootGroup->addChild(proxy);
loadedCount++;
std::cout << "Added reference to: " << nodeFile.string() << std::endl;
}
if (loadedCount == 0)
{
std::cerr << "Error: no valid tile root nodes found in " << inputPath << std::endl;
return false;
}
if (!osgDB::writeNodeFile(*rootGroup, outPath.string()))
{
std::cerr << "Error: failed to write output file " << outPath << std::endl;
return false;
}
std::cout << "\nSuccessfully created " << outPath.filename().string()
<< " with " << loadedCount << " children." << std::endl;
if (skippedCount > 0)
{
std::cout << "Skipped " << skippedCount << " directories due to missing or invalid root files." << std::endl;
}
return true;
}
运行结果
执行程序后,生成 Mongkok.osgb 顶层索引文件。再次使用 osgviewer 打开该文件,即可看到整个旺角区域的完整模型:

通过这种方式,我们无需合并原始几何数据,即可实现分块模型的统一加载与全局预览。
七、常见问题与注意事项
- OSGB文件缺失纹理:纹理通常以外部
.jpg/.png文件存储,与OSGB在同一目录或Data子目录下。若缺少纹理,检查osgt中的FileName路径。 - 合并后模型飞走:不同瓦片的局部坐标系原点不同,直接合并会导致位置错乱。需要先提取每个瓦片的
UserCenter或包围盒,统一变换到同一坐标系。 - 大模型内存不足:使用
osgviewer --stats观察内存占用;若需转换,建议分批处理或采用流式加载(如3D Tiles)。 - Smart3D标准目录结构:要求
Data/文件夹下按Tile_+X_+Y/存放瓦片,且根目录有metadata.xml。若手头是散乱文件,可编写脚本根据文件名或包围盒重新组织。
八、扩展资源
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)