【OSG学习笔记】Day 23: ClipNode(动态裁剪)

osg::ClipNode

在 OpenSceneGraph(OSG)三维开发中,模型动态裁剪是可视化、剖切、内部结构展示的核心功能。

osg::ClipNode 正是 OSG 提供的专用裁剪节点,可以轻松实现模型剖切、区域可见性控制、动态切面等高级渲染效果。

本文将从继承关系、核心原理、使用方法、完整代码示例 四个维度,带你彻底掌握 osg::ClipNode


什么是 osg::ClipNode

osg::ClipNode 是 OSG 中专门用于裁剪空间渲染 的节点类。

它通过定义裁剪平面(ClipPlane),对场景中的几何体进行"切割",只保留平面一侧的可见部分,常用于:

  • 模型剖切视图
  • 动态切面展示内部结构
  • 区域可见性过滤
  • 医学影像、机械模型、建筑模型剖面展示

一句话总结:
ClipNode = 场景的"空间切割器",通过平面控制模型哪些部分可见。


osg::ClipNode 完整继承关系

ClipNode 属于 OSG 场景树节点体系,继承链非常清晰:

复制代码
osg::Object
   ↓
osg::Node
   ↓
osg::Group
   ↓
osg::ClipNode

各父类作用(必须理解)

  1. osg::Object

    所有 OSG 对象的基类,提供引用计数、智能指针、克隆、命名能力。

  2. osg::Node

    场景树节点基类,提供包围盒、更新回调、渲染状态基础能力。

  3. osg::Group

    组节点基类,可以挂载子节点,是组合场景的基础。

  4. osg::ClipNode

    继承组节点能力,自动管理裁剪平面与裁剪状态,将裁剪效果作用于所有子节点。


osg::ClipNode 核心原理

ClipNode 底层基于 OpenGL 裁剪平面(GL_CLIP_PLANE0~5) 实现:

  1. 创建 1~6 个裁剪平面
  2. 将平面参数传入 GPU
  3. 渲染时只保留平面正面的几何体
  4. 自动通过 StateSet 管理裁剪开关与状态

它最大的优势:

  • 不用修改模型数据
  • 性能极高(GPU 硬件裁剪)
  • 可动态移动/旋转平面
  • 可嵌套、可组合、可继承

osg::ClipNode 常用 API

cpp 复制代码
// 创建盒式裁剪(自动生成6个面,形成包围盒裁剪)
void createClipBox(const BoundingBox& bbox);

// 添加/移除裁剪平面
void addClipPlane(ClipPlane* p);
void removeClipPlane(unsigned int i);

// 获取/设置裁剪状态
StateSet* getStateSet();

// 启用/禁用自身拣选(避免裁剪平面被意外剔除)
void setCullingActive(false);

完整实战代码

以下代码基于标准 OSG 工程,实现:

  • 动态旋转裁剪
  • 线框渲染
  • 实体+裁剪对比显示
cpp 复制代码
#include <osg/Group>
#include <osg/StateSet>
#include <osg/PolygonMode>
#include <osg/MatrixTransform>
#include <osg/ClipNode>
#include <osg/ClipPlane>
#include <osg/BoundingSphere>
#include <osg/BoundingBox>
#include <osg/NodeCallback>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgUtil/Optimizer>
#include <iostream>

// 动态旋转回调:让裁剪平面持续旋转,实现动态剖切
class ClipRotateCallback : public osg::NodeCallback
{
public:
    ClipRotateCallback(const osg::Vec3d& center, const osg::Vec3& axis, float speed)
        : _center(center), _axis(axis), _speed(speed), _time(0.0), _lastTime(0.0) {}

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
    {
        osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(node);
        if (mt && nv)
        {
            double delta = nv->getFrameStamp()->getReferenceTime() - _lastTime;
            _time += delta;
            _lastTime = nv->getFrameStamp()->getReferenceTime();

            // 旋转矩阵
            osg::Quat quat(osg::DegreesToRadians(_speed * _time), _axis);
            mt->setMatrix(osg::Matrix::translate(_center) *
                          osg::Matrix::rotate(quat) *
                          osg::Matrix::translate(-_center));
        }
        traverse(node, nv);
    }
private:
    osg::Vec3d _center;
    osg::Vec3 _axis;
    float _speed;
    double _time;
    double _lastTime;
};

// 创建裁剪场景
osg::ref_ptr<osg::Node> createClipScene(osg::ref_ptr<osg::Node> model)
{
    osg::ref_ptr<osg::Group> root = new osg::Group();

    // ====================== 1. 线框模式 ======================
    osg::ref_ptr<osg::StateSet> ssWire = new osg::StateSet();
    osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode();
    pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
    ssWire->setAttributeAndModes(pm, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);

    osg::ref_ptr<osg::Group> wireFrame = new osg::Group();
    wireFrame->setStateSet(ssWire);
    wireFrame->addChild(model);
    root->addChild(wireFrame);

    // ====================== 2. 动态裁剪节点 ======================
    osg::ref_ptr<osg::MatrixTransform> animTrans = new osg::MatrixTransform();
    animTrans->setUpdateCallback(new ClipRotateCallback(
        model->getBound().center(),
        osg::Vec3(0,0,1),   // 旋转轴
        45.0f               // 旋转速度
    ));

    // 创建 ClipNode
    osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode();
    osg::BoundingBox box;
    box.expandBy(model->getBound());
    clipNode->createClipBox(box);       // 自动生成6个裁剪面
    clipNode->setCullingActive(false);  // 禁止剔除,保证裁剪生效

    animTrans->addChild(clipNode);
    clipNode->addChild(model);
    root->addChild(animTrans);

    // ====================== 3. 应用裁剪效果到实体模型 ======================
    osg::ref_ptr<osg::Group> clippedModel = new osg::Group();
    clippedModel->setStateSet(clipNode->getStateSet());
    clippedModel->addChild(model);
    root->addChild(clippedModel);

    return root;
}

int main()
{
    osgViewer::Viewer viewer;
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg");

    if (!model)
    {
        std::cout << "模型加载失败!" << std::endl;
        return -1;
    }

    osg::ref_ptr<osg::Node> scene = createClipScene(model);
    viewer.setSceneData(scene);
    return viewer.run();
}

代码核心逻辑讲解

1. 动态裁剪回调

让裁剪平面自动旋转,实现模型动态剖切效果。

2. createClipBox

自动根据模型包围盒生成6个裁剪平面,形成盒式裁剪。

3. setCullingActive(false)

非常关键:
禁止裁剪节点被视锥体裁掉,保证裁剪平面永远生效。

4. 裁剪状态共享

clippedModel->setStateSet(clipNode->getStateSet())

让多个模型共用同一套裁剪状态,性能极高


效果说明

运行后你将看到三部分模型:

  1. 左侧:线框模型
  2. 中间:动态旋转裁剪模型
  3. 右侧:应用了裁剪的实体模型

可以清晰看到模型内部结构,是机械、建筑、医疗可视化的标准方案。


总结

  • osg::ClipNode :OSG 官方标准空间裁剪节点
  • 继承关系Object → Node → Group → ClipNode
  • 核心能力:硬件加速裁剪、动态平面、多模型共享状态。
  • 使用场景:模型剖切、切面展示、区域可见、内部结构查看。
相关推荐
葡萄成熟时 !2 小时前
JAVA 常用API学习笔记
java·笔记·学习
DevOpenClub2 小时前
个人公开市场研究笔记功能需求文档
笔记
别动我齐刘海4 小时前
机器人运动控制学习2——基础进阶
c++·人工智能·神经网络·学习·目标检测·机器学习·机器人
0566466 小时前
agent学习——流式响应与文本切分
网络·python·学习
for_ever_love__7 小时前
python基础语法学习: 面向对象的三大特性
开发语言·python·学习
不会代码的小猴7 小时前
7. JSON
开发语言·c++·笔记·qt·算法·json
hunterandroid8 小时前
Android WebView JSBridge 治理实战:从线上白屏崩溃到协议化通信
android·前端
一个用户名i9 小时前
【Compose 系列】第 1 篇:认识 Compose,为什么要学它
android·android jetpack
Canmag 兴隆磁性9 小时前
C 系列充磁机
c语言·开发语言·学习·永磁材料·充磁·退磁
GGMM7899 小时前
富士 EP‑3955D 变频器主板维修调试笔记
笔记·变频器·变频器维修