【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
  • 核心能力:硬件加速裁剪、动态平面、多模型共享状态。
  • 使用场景:模型剖切、切面展示、区域可见、内部结构查看。
相关推荐
Eagsen CEO2 小时前
如何让 Gemini 在 Android Studio 中顺利工作
android·ide·android studio
VelinX2 小时前
【个人学习||算法】多维动态规划
学习·算法·动态规划
老鱼说AI2 小时前
大模型学习与面试精讲第六期:损失函数篇
人工智能·深度学习·神经网络·学习·机器学习·语言模型
炽烈小老头2 小时前
【 每天学习一点算法 2026/03/30】跳跃游戏
学习·算法
Lufeidata3 小时前
go语言学习记录-入门阶段
前端·学习·golang
ywf12153 小时前
FlinkCDC实战:将 MySQL 数据同步至 ES
android·mysql·elasticsearch
计算机安禾3 小时前
【数据结构与算法】第15篇:队列(二):链式队列的实现与应用
c语言·开发语言·数据结构·c++·学习·算法·visual studio