【OSG学习笔记】Day 12: OccluderNode(遮挡剔除)

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
层级解析:
osg::Object:OSG 所有对象的基类,提供引用计数、内存管理能力;osg::Node:场景节点基类,具备包围盒计算、节点名称、状态集等基础能力;osg::Group:组节点,可以添加子节点(这也是 OccluderNode 能附加可视化几何体的原因);osg::OccluderNode:最终遮挡节点,新增遮挡器绑定、遮挡开关等专属接口。
配套核心类依赖
OccluderNode 无法单独工作,必须配合以下类实现完整功能:
osg::ConvexPlanarOccluder:凸平面遮挡器,定义真正的遮挡多边形;osg::ConvexPlanarPolygon:凸平面多边形,存储遮挡平面的顶点数据;osg::BoundingBox/BoundingSphere:包围盒,自动计算模型边界,生成贴合的遮挡面。
实战代码解析
- 创建 OccluderNode 节点
- 绑定 ConvexPlanarOccluder 遮挡器
- 设置遮挡平面顶点
- 添加可视化几何体(调试用)
- 根据模型包围盒生成多面遮挡墙
- 加载场景并启用遮挡剔除渲染
模块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 工作原理
- 渲染前期:OSG 遍历场景树,收集所有
OccluderNode及其遮挡平面; - 遮挡检测:渲染器判断物体是否完全处于遮挡平面后方;
- 自动剔除:被完全遮挡的物体,直接跳过绘制指令;
- 性能提升:减少 GPU 绘制调用,大幅降低渲染压力。
⚠️ 重要说明:
- 遮挡剔除仅对完全遮挡的物体生效;
- 遮挡平面必须是闭合、凸多边形;
- 可视化透明面板不影响遮挡逻辑,仅用于调试。
适用场景
- 室内漫游:墙体作为遮挡面,剔除室外物体;
- 智慧城市:高楼遮挡后方建筑;
- 大型仿真:机械、厂房内部遮挡优化;
- 游戏场景:山体、建筑遮挡剔除。
总结
osg::OccluderNode 是 OSG 高性能渲染的必备工具,依托 Group 继承能力,可灵活挂载可视化对象;配合 ConvexPlanarOccluder 实现精准遮挡剔除。
掌握 OccluderNode,你就能轻松解决 OSG 大规模场景的渲染性能问题,是进阶 OSG 开发的核心知识点。
核心知识点速记
- 继承关系:
Object → Node → Group → OccluderNode - 核心依赖:
ConvexPlanarOccluder+ConvexPlanarPolygon - 关键步骤:创建节点 → 绑定遮挡器 → 设置顶点 → 添加可视化
- 核心价值:自动剔除无效渲染,大幅提升场景帧率

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


所有评论(0)