在这里插入图片描述

OccluderNode

在大规模三维场景渲染(如智慧城市、游戏、仿真系统)中,遮挡剔除是提升渲染性能的关键技术——它能让渲染引擎自动忽略“被前方物体完全遮挡、相机无法看到”的模型,减少无效绘制,大幅提升帧率。

OpenSceneGraph(OSG)提供了原生的遮挡剔除解决方案,核心就是 osg::OccluderNode 遮挡节点。本文结合完整可运行的实战代码,深度讲解 OccluderNode 的用法、类继承关系、工作原理,并手把手实现模型自动遮挡包围效果。

OccluderNode 核心基础概念

1. 什么是 OccluderNode?

osg::OccluderNode 是 OSG 中专门用于定义遮挡区域的场景节点,它本身不参与场景的视觉渲染(可附加可视化几何体方便调试),核心作用是告诉 OSG 渲染器:这个平面后方的物体被完全遮挡,无需渲染

它是 OSG 自动遮挡剔除机制的入口,配合凸平面遮挡器,实现高效的视锥体剔除+遮挡剔除双优化。

2. 核心作用

  • 标记场景中的遮挡平面(墙面、挡板、山体等)
  • 驱动 OSG 自动剔除被遮挡的物体
  • 无侵入式优化,无需修改原有模型数据
  • 支持动态场景,包围盒自动适配模型大小

OccluderNode 类继承关系(关键)

理解继承关系,才能掌握 OccluderNode 的底层能力边界。OSG 采用单根继承体系,OccluderNode 的继承链如下:

osg::Object
  └── osg::Node
       └── osg::Group
            └── osg::OccluderNode

层级解析:

  1. osg::Object:OSG 所有对象的基类,提供引用计数、内存管理能力;
  2. osg::Node:场景节点基类,具备包围盒计算、节点名称、状态集等基础能力;
  3. osg::Group:组节点,可以添加子节点(这也是 OccluderNode 能附加可视化几何体的原因);
  4. osg::OccluderNode:最终遮挡节点,新增遮挡器绑定、遮挡开关等专属接口。

配套核心类依赖

OccluderNode 无法单独工作,必须配合以下类实现完整功能:

  • osg::ConvexPlanarOccluder:凸平面遮挡器,定义真正的遮挡多边形
  • osg::ConvexPlanarPolygon:凸平面多边形,存储遮挡平面的顶点数据;
  • osg::BoundingBox/BoundingSphere:包围盒,自动计算模型边界,生成贴合的遮挡面。

实战代码解析

  1. 创建 OccluderNode 节点
  2. 绑定 ConvexPlanarOccluder 遮挡器
  3. 设置遮挡平面顶点
  4. 添加可视化几何体(调试用)
  5. 根据模型包围盒生成多面遮挡墙
  6. 加载场景并启用遮挡剔除渲染

模块1:创建单个遮挡节点(核心函数)

// 输入:4个顶点,输出:配置完成的遮挡节点
osg::ref_ptr<osg::Node> createOccluder(const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3, const osg::Vec3& v4)
{
    // 1. 创建遮挡节点(继承自Group,可挂载子节点)
    osg::ref_ptr<osg::OccluderNode> occluderNode = new osg::OccluderNode();

    // 2. 创建凸平面遮挡器(遮挡逻辑核心)
    osg::ref_ptr<osg::ConvexPlanarOccluder> cpo = new osg::ConvexPlanarOccluder;

    // 3. 将遮挡器关联到遮挡节点(关键绑定)
    occluderNode->setOccluder(cpo.get());
    occluderNode->setName("occluder");

    // 4. 设置遮挡平面的4个顶点,构成四边形
    osg::ConvexPlanarPolygon& occluder = cpo->getOccluder();
    occluder.add(v1);
    occluder.add(v2);
    occluder.add(v3);
    occluder.add(v4);

    // ==================== 以下为可视化遮挡板(仅调试,不影响遮挡) ====================
    // 创建半透明四边形,直观看到遮挡范围
    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
    geom->setVertexArray(new osg::Vec3Array(occluder.getVertexList().begin(), occluder.getVertexList().end()));
    
    // 半透明白色,关闭光照,开启混合
    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
    (*colors)[0].set(1.0f, 1.0f, 1.0f, 0.5f);
    geom->setColorArray(colors.get());
    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));

    // 渲染状态设置
    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
    stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
    geom->setStateSet(stateset.get());

    // 可视化几何体作为子节点挂载到遮挡节点
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(geom.get());
    occluderNode->addChild(geode.get());

    return occluderNode.get();
}

在这里插入图片描述

关键要点:
  • 遮挡逻辑 ≠ 可视化模型:半透明四边形仅用于显示,真正的遮挡由 ConvexPlanarOccluder 计算;
  • 必须绑定遮挡器setOccluder() 是 OccluderNode 生效的核心;
  • 遮挡平面必须是凸多边形:OSG 仅支持凸平面遮挡,四边形是最常用格式。

模块2:自动围绕模型生成遮挡墙

我们可以根据模型的自动包围盒,生成前、后、左、右4个遮挡面,实现模型全包围遮挡:

osg::ref_ptr<osg::Group> createOccludersAroundModel(osg::ref_ptr<osg::Node> model)
{
    osg::ref_ptr<osg::Group> scene = new osg::Group();
    scene->addChild(model.get());

    // 1. 获取模型球形包围盒
    const osg::BoundingSphere bs = model->getBound();
    osg::BoundingBox bb;
    bb.expandBy(bs); // 转换为轴对齐包围盒

    // 2. 批量创建4个方向的遮挡面
    scene->addChild(createOccluder(bb.corner(0), bb.corner(1), bb.corner(5), bb.corner(4))); // 前
    scene->addChild(createOccluder(bb.corner(1), bb.corner(3), bb.corner(7), bb.corner(5))); // 右
    scene->addChild(createOccluder(bb.corner(2), bb.corner(0), bb.corner(4), bb.corner(6))); // 左
    scene->addChild(createOccluder(bb.corner(3), bb.corner(2), bb.corner(6), bb.corner(7))); // 后

    return scene;
}
优势:
  • 全自动适配任意模型,无需手动计算遮挡位置;
  • 遮挡面完全贴合模型边界,剔除精度最高。

模块3:主函数(场景加载与渲染)

int main()
{
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
    osg::ref_ptr<osg::Group> root = new osg::Group();

    // 加载OSG自带牛模型
    osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
    root->addChild(createOccludersAroundModel(node.get()));

    // 场景优化(提升遮挡剔除效率)
    osgUtil::Optimizer optimizer;
    optimizer.optimize(root.get());

    viewer->setSceneData(root.get());
    viewer->realize();
    viewer->run();

    return 0;
}

OccluderNode 工作原理

  1. 渲染前期:OSG 遍历场景树,收集所有 OccluderNode 及其遮挡平面;
  2. 遮挡检测:渲染器判断物体是否完全处于遮挡平面后方;
  3. 自动剔除:被完全遮挡的物体,直接跳过绘制指令;
  4. 性能提升:减少 GPU 绘制调用,大幅降低渲染压力。

⚠️ 重要说明:

  • 遮挡剔除仅对完全遮挡的物体生效
  • 遮挡平面必须是闭合、凸多边形
  • 可视化透明面板不影响遮挡逻辑,仅用于调试。

适用场景

  1. 室内漫游:墙体作为遮挡面,剔除室外物体;
  2. 智慧城市:高楼遮挡后方建筑;
  3. 大型仿真:机械、厂房内部遮挡优化;
  4. 游戏场景:山体、建筑遮挡剔除。

总结

osg::OccluderNode 是 OSG 高性能渲染的必备工具,依托 Group 继承能力,可灵活挂载可视化对象;配合 ConvexPlanarOccluder 实现精准遮挡剔除。

掌握 OccluderNode,你就能轻松解决 OSG 大规模场景的渲染性能问题,是进阶 OSG 开发的核心知识点。


核心知识点速记

  1. 继承关系Object → Node → Group → OccluderNode
  2. 核心依赖ConvexPlanarOccluder + ConvexPlanarPolygon
  3. 关键步骤:创建节点 → 绑定遮挡器 → 设置顶点 → 添加可视化
  4. 核心价值:自动剔除无效渲染,大幅提升场景帧率

在这里插入图片描述

Logo

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

更多推荐