一、简介

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.osgb3143415263404253.osgb等),说明数据集被分成了多个独立的地块,每个文件都是其对应地块的根节点。此时需要将这些根节点组合成一个总场景(例如通过一个顶层Group或3D Tiles的tileset)。


六、重建根节点:合并分块,全局预览

在实际生产场景中,Smart3D 等软件导出的倾斜摄影模型往往被切割成多个独立的数据块。例如,香港旺角区域的 OSGB 数据目录结构如下:

在这里插入图片描述

每个子目录代表一个独立的地块,其内部包含与目录同名的 .osgb 根节点文件。使用 osgviewer 只能单独加载某一个地块,例如:

osgviewer ./Data/Mongkok/Tile_+002_+014/Tile_+002_+014.osgb

显示效果仅为局部区域,无法快速纵览全局。

在这里插入图片描述

解决方案:构建顶层索引文件

若要一次性查看整个旺角区域,需要创建一个顶层索引文件,将所有地块的根节点引用汇总。该索引文件本身不存储几何数据,而是通过 osg::ProxyNode 记录各个子文件的路径,加载时再由 OSG 按需调度。

实现思路
  1. 遍历输入目录下的所有子文件夹;
  2. 对于每个子文件夹,检查是否存在与文件夹同名的 .osgb 文件;
  3. 若存在,创建一个 osg::ProxyNode 节点,将其文件名设置为该 .osgb 文件的路径;
  4. 将所有 ProxyNode 添加到一个 osg::Group 中;
  5. 将 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 打开该文件,即可看到整个旺角区域的完整模型:

在这里插入图片描述

通过这种方式,我们无需合并原始几何数据,即可实现分块模型的统一加载与全局预览。

七、常见问题与注意事项

  1. OSGB文件缺失纹理:纹理通常以外部.jpg/.png文件存储,与OSGB在同一目录或Data子目录下。若缺少纹理,检查osgt中的FileName路径。
  2. 合并后模型飞走:不同瓦片的局部坐标系原点不同,直接合并会导致位置错乱。需要先提取每个瓦片的UserCenter或包围盒,统一变换到同一坐标系。
  3. 大模型内存不足:使用osgviewer --stats观察内存占用;若需转换,建议分批处理或采用流式加载(如3D Tiles)。
  4. Smart3D标准目录结构:要求Data/文件夹下按Tile_+X_+Y/存放瓦片,且根目录有metadata.xml。若手头是散乱文件,可编写脚本根据文件名或包围盒重新组织。

八、扩展资源

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐