【OSG学习笔记】Day 5: Group类与PositionAttitudeTransform类

0 阅读7分钟

去除图片水印 (1).png

OSG核心节点:osg::Group与PositionAttitudeTransform

OpenSceneGraph(OSG)作为高性能的跨平台3D图形引擎,其核心设计思想是场景图(Scene Graph) ——通过树形结构组织所有渲染对象,而osg::Group及其子类是构建场景图的基石。

本文将从继承关系、核心功能、代码实践三个维度,详解osg::Group类,并重点分析其重要子类PositionAttitudeTransform(PAT)的使用场景与实现逻辑。

osg::Group:场景图的“骨架”节点

1 继承关系:OSG节点体系的核心分支

osg::Group是OSG场景图中组节点的基类,其完整继承链如下:

osg::Referenced(引用计数基类)
    ↓
osg::Object(OSG对象基类,提供命名/克隆等基础功能)
    ↓
osg::Node(所有场景节点的根类)
    ↓
osg::Group(组节点基类)
  • 核心父类说明
    • osg::Referenced:为所有OSG对象提供线程安全的引用计数内存管理,避免手动new/delete导致的内存泄漏;
    • osg::Node:定义了场景节点的核心接口(如遍历、渲染状态、更新回调),是所有可加入场景图的对象的基类;
    • osg::Group:在osg::Node基础上扩展了子节点管理能力,是唯一能包含其他节点的核心类。

2 核心功能:场景图的“容器”

osg::Group的核心价值是管理子节点集合,它本身不包含可渲染的几何数据,仅负责组织、遍历和传递渲染状态,主要能力包括:

  1. 子节点增删查改:提供addChild()/removeChild()/getChild()/getNumChildren()等接口,支持动态维护子节点列表;
  2. 渲染状态继承:组节点设置的渲染状态(如纹理、材质)会自动传递给所有子节点(可通过StateSet控制);
  3. 遍历控制:支持设置NodeVisitor遍历回调,自定义子节点的遍历逻辑(如裁剪、拣选);
  4. 线程安全:基于osg::Referenced的线程安全设计,多线程环境下增删子节点仍能保证稳定。

3 关键特性与使用场景

  • 无数量限制:一个osg::Group可包含任意数量的子节点(包括其他osg::Grouposg::Geode),形成多层级树形结构;
  • 场景分块管理:常用于按功能划分场景(如“地形组”“模型组”“特效组”),便于批量控制显隐、更新;
  • 不可渲染osg::Group本身无几何数据,若需渲染需结合osg::Geode(几何节点)使用。

PositionAttitudeTransform:节点变换的“核心工具”

PositionAttitudeTransform(简称PAT)是osg::Group的最重要子类之一,专门用于控制子节点的空间变换(位置、姿态、缩放),是OSG中实现模型位移、旋转、缩放的核心类。

1 继承关系:基于Group的变换扩展

PAT的继承链在osg::Group基础上进一步扩展:

osg::Referenced
    ↓
osg::Objectosg::Node
    ↓
osg::Grouposg::Transform(变换节点基类)
    ↓
osg::PositionAttitudeTransform
  • 关键中间类:osg::Transformosg::Transform是所有变换节点的基类,定义了“矩阵变换”的核心接口(computeLocalToWorldMatrix()/computeWorldToLocalMatrix()),但未封装具体的变换参数;
  • PositionAttitudeTransform:在osg::Transform基础上,将变换拆解为位置(Position)、姿态(Attitude)、缩放(Scale) 三个直观参数,简化了3D空间变换的使用。

2 核心功能:三维空间变换的“封装器”

PAT将复杂的矩阵变换封装为三个易用的参数,避免手动计算变换矩阵,核心参数如下:

参数类型作用
Positionosg::Vec3控制子节点在世界坐标系中的位置(X/Y/Z轴偏移)
Attitudeosg::Quat控制子节点的姿态(旋转),基于四元数实现无万向节死锁的3D旋转
Scaleosg::Vec3控制子节点在X/Y/Z轴上的缩放比例(1.0为原始大小,0.5为缩小一半)
Pivot Pointosg::Vec3旋转/缩放的中心点(默认是节点局部坐标系原点)

3 核心优势

  1. 直观易用:无需手动构建4×4变换矩阵,直接设置位置、旋转、缩放参数即可;
  2. 四元数旋转:基于osg::Quat的姿态控制,避免欧拉角的万向节死锁问题;
  3. 子节点批量变换:PAT的变换会作用于所有子节点,可批量控制多个模型的空间状态;
  4. 动态更新:支持运行时修改变换参数(如动画中实时调整位置/旋转),立即生效。

代码实践:Group与PAT的综合使用

下面通过完整示例,展示如何基于osg::Group构建场景图,并通过PositionAttitudeTransform实现模型的空间变换:

源码库:osg_transform

1 完整代码

#include <osgViewer/Viewer>
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/Geode>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/Notify>

// 创建一个带PAT变换的模型节点
osg::ref_ptr<osg::Node> createTransformedModel(const std::string& modelPath, 
                                               const osg::Vec3& pos, 
                                               const osg::Vec3& scale,
                                               const osg::Quat& attitude)
{
    // 1. 加载模型(返回osg::Node,可能是Group/Geode)
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(modelPath);
    if (!model)
    {
        osg::notify(osg::FATAL) << "模型加载失败:" << modelPath << std::endl;
        return nullptr;
    }

    // 2. 创建PAT变换节点
    osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
    pat->setPosition(pos);         // 设置位置
    pat->setScale(scale);          // 设置缩放
    pat->setAttitude(attitude);    // 设置旋转姿态

    // 3. 将模型添加为PAT的子节点(变换作用于模型)
    pat->addChild(model.get());

    return pat;
}

int main()
{
    // 1. 创建场景根节点(osg::Group)
    osg::ref_ptr<osg::Group> root = new osg::Group();
    root->setName("SceneRoot"); // 设置节点名称(osg::Object的能力)

    // 2. 定义变换参数
    osg::Vec3 pos1(-10.0f, 0.0f, 0.0f);  // 左侧模型位置
    osg::Vec3 scale1(0.5f, 0.5f, 0.5f);  // 缩小为0.5倍
    osg::Quat rot1(0.0f, osg::Vec3(0, 1, 0)); // 无旋转(绕Y轴0度)

    osg::Vec3 pos2(10.0f, 0.0f, 0.0f);   // 右侧模型位置
    osg::Vec3 scale2(1.0f, 1.0f, 1.0f);  // 原始大小
    osg::Quat rot2(osg::PI/2, osg::Vec3(0, 1, 0)); // 绕Y轴旋转90度

    // 3. 创建两个带不同变换的模型节点
    osg::ref_ptr<osg::Node> model1 = createTransformedModel("cow.osg", pos1, scale1, rot1);
    osg::ref_ptr<osg::Node> model2 = createTransformedModel("cow.osg", pos2, scale2, rot2);

    if (model1 && model2)
    {
        // 4. 将变换节点添加到根节点(osg::Group的核心能力)
        root->addChild(model1.get());
        root->addChild(model2.get());
    }

    // 5. 优化场景图(提升渲染性能)
    osgUtil::Optimizer optimizer;
    optimizer.optimize(root.get());

    // 6. 创建Viewer并运行
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
    viewer->setSceneData(root.get());
    viewer->realize(); // 初始化窗口
    return viewer->run(); // 启动渲染循环
}

2 代码解析

  1. 场景结构: 示例中场景图的树形结构为: root (osg::Group) ├─ PAT1 (PositionAttitudeTransform) │ └─ cow.osg (模型节点) └─ PAT2 (PositionAttitudeTransform) └─ cow.osg (模型节点) 同一个模型被两个PAT节点包裹,实现不同的空间变换,且共享模型数据(OSG引用计数自动管理)。

  2. 核心API说明

    • setPosition(osg::Vec3(x,y,z)):设置PAT节点的世界位置,子节点会跟随位移;
    • setScale(osg::Vec3(x,y,z)):沿X/Y/Z轴独立缩放,示例中model1缩小为0.5倍;
    • setAttitude(osg::Quat(angle, axis)):基于四元数旋转,osg::PI/2表示90度,osg::Vec3(0,1,0)表示绕Y轴旋转;
    • root->addChild()osg::Group的核心接口,将变换后的节点加入场景根节点。
  3. 运行效果: 程序运行后会显示两个牛模型:

    • 左侧牛:位置X=-10,缩放0.5倍,无旋转;
    • 右侧牛:位置X=10,原始大小,绕Y轴旋转90度。

image.png

总结

  1. osg::Group 是OSG场景图的“骨架”,核心作用是组织子节点,本身不可渲染,但支持渲染状态继承和批量控制,是构建复杂场景的基础;
  2. PositionAttitudeTransformosg::Group的核心子类,封装了3D空间变换的核心逻辑,通过位置、姿态、缩放三个参数简化了矩阵变换的使用,是实现模型位移/旋转/缩放的首选工具;
  3. 继承关系的设计体现了OSG的模块化思想:Referenced管内存、Node管节点基础能力、Group管子节点管理、Transform管变换、PAT管具体的空间变换参数,层层封装,兼顾易用性和扩展性。

掌握osg::GroupPositionAttitudeTransform的使用,是构建OSG场景图的核心基础,在此之上可进一步扩展到更复杂的变换(如MatrixTransform)、节点回调(如UpdateCallback)等高级功能。 去除图片水印.png