【OSG学习笔记】Day 39: NodeCallback(帧回调机制)

NodeCallback 帧回调机制

在 OpenSceneGraph(OSG)开发中,NodeCallback 是实现模型动画、实时更新、交互控制的核心技术。它可以让我们在渲染的每一帧自动执行自定义逻辑,无需手动编写循环,是 OSG 场景树动态控制的基础。

本文结合模型旋转动画完整代码,深度讲解 NodeCallback 的原理、继承关系、使用方法,帮你彻底掌握 OSG 帧回调的核心用法。

前置知识

要理解 NodeCallback,首先要明确 OSG 中核心类的层级关系,这是所有用法的基础:

1. NodeCallback 继承体系

复制代码
osg::Referenced  // OSG 智能指针管理基类(自动内存管理)
        └── osg::Callback  // 回调基类
                └── osg::NodeCallback  // 节点回调(本文核心)
  • NodeCallback 是专门为场景节点设计的回调类,用于节点的实时更新
  • 我们自定义回调必须继承自 NodeCallback ,并重写 operator() 方法

2. MatrixTransform 节点继承体系

复制代码
osg::Node  // 所有场景节点的基类
        └── osg::Group  // 可挂载子节点的父节点
                └── osg::Transform  // 坐标变换基类
                        └── osg::MatrixTransform  // 矩阵变换节点(控制旋转/平移/缩放)
  • MatrixTransform功能型节点,专门给子节点施加坐标变换
  • 它是 NodeCallback 最常用的绑定对象(因为我们几乎都用它做动画)

核心原理

1. 什么是 NodeCallback?

NodeCallback 是 OSG 提供的节点帧回调接口

  • 绑定到任意 osg::Node(包括 MatrixTransform、Group、模型节点)
  • 渲染每一帧 ,OSG 遍历场景树时,会自动调用绑定的回调
  • 重写 operator() 方法,即可实现自定义逻辑(旋转、移动、交互等)

2. 调用时机(OSG 一帧渲染流程)

  1. OSG 启动渲染循环 → 遍历场景树
  2. 遇到绑定了 NodeCallback 的节点
  3. 自动执行回调的 operator() 方法
  4. 执行完自定义逻辑后,继续遍历子节点
  5. 完成渲染,展示画面

核心特性:绑定在哪个节点,就为哪个节点每帧执行回调

3. 必须掌握的 2 个核心方法

cpp 复制代码
// 1. 重写的核心方法:每一帧自动调用
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);

// 2. 必须调用的方法:继续遍历子节点(不写会导致场景卡住/不渲染)
traverse(node, nv);

完整实战代码

cpp 复制代码
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <iostream>

// ===================== 自定义回调类:继承 NodeCallback =====================
class RotateCallback : public osg::NodeCallback
{
public:
    // 构造函数:初始化旋转角度为0
    RotateCallback() : _rotateAngle(0.0) {}

    // 核心重写方法:OSG 每一帧自动调用此函数
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) override
    {
        // 1. 将节点转换为 MatrixTransform(必须转换,才能修改矩阵)
        osg::MatrixTransform* transform = dynamic_cast<osg::MatrixTransform*>(node);
        if (transform)
        {
            // 2. 定义旋转矩阵:绕Z轴旋转(弧度制)
            osg::Matrix rotateMatrix;
            rotateMatrix.makeRotate(_rotateAngle, osg::Vec3(0.0f, 0.0f, 1.0f));

            // 3. 为变换节点设置旋转矩阵
            transform->setMatrix(rotateMatrix);

            // 4. 角度递增,实现连续旋转
            _rotateAngle += 0.01f;
        }

        // 关键:必须调用 traverse,继续遍历子节点,否则模型不渲染
        traverse(node, nv);
    }

private:
    double _rotateAngle;  // 记录旋转角度(成员变量,持久化存储)
};

// ===================== 主函数:场景搭建 =====================
int main()
{
    // 1. 创建OSG查看器
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

    // 2. 创建根节点
    osg::ref_ptr<osg::Group> root = new osg::Group();

    // 3. 加载模型(OSG自带示例模型 cow.osg)
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cow.osg");
    if (!model)
    {
        std::cerr << "模型加载失败!" << std::endl;
        return -1;
    }

    // 4. 创建矩阵变换节点(控制模型旋转)
    osg::ref_ptr<osg::MatrixTransform> mtNode = new osg::MatrixTransform();
    mtNode->addChild(model);  // 模型作为子节点

    // 5. 核心:为 MatrixTransform 绑定自定义 NodeCallback
    mtNode->setUpdateCallback(new RotateCallback());

    // 6. 节点添加到场景树
    root->addChild(mtNode);

    // 7. 优化场景
    osgUtil::Optimizer optimizer;
    optimizer.optimize(root);

    // 8. 设置场景并运行
    viewer->setSceneData(root);
    viewer->realize();
    return viewer->run();
}

代码核心解析(NodeCallback 重点)

1. 自定义回调类的编写规则

  1. 必须继承 osg::NodeCallback
  2. 重写 operator() 方法(这是帧回调的入口)
  3. 成员变量存储状态(如旋转角度,跨帧持久化)
  4. 必须调用 traverse(node, nv)

2. 为什么回调要绑定在 MatrixTransform 上?

  • 我们要修改坐标变换,而不是修改模型本身
  • 回调绑定到 MatrixTransform,才能在 operator() 中获取到该节点
  • 模型节点本身没有矩阵变换功能,无法实现旋转

3. 内存管理说明

  • OSG 所有回调、节点都继承自 osg::Referenced
  • 使用 osg::ref_ptr 智能指针,无需手动 delete,自动释放内存

NodeCallback 常用扩展场景

掌握基础用法后,你可以用 NodeCallback 实现所有动态效果:

  1. 模型位移动画 :修改矩阵的 makeTranslate
  2. 缩放动画 :修改矩阵的 makeScale
  3. 交互控制:键盘/鼠标控制节点状态
  4. 骨骼动画:嵌套 MatrixTransform + 多个回调
  5. 性能优化:按需更新,而非每帧执行

总结

  1. NodeCallback 是 OSG 帧回调核心,每一帧自动执行
  2. 继承关系:osg::Referenced → osg::Callback → osg::NodeCallback
  3. 核心方法:重写 operator() + 必须调用 traverse()
  4. 最佳实践:回调绑定 MatrixTransform 节点,实现模型动画
  5. 价值:解耦「动画逻辑」和「模型渲染」,是 OSG 开发必备技能
相关推荐
努力努力再努力FFF16 小时前
运维工程师想学习AI来提升系统自动化水平,该怎么切入?
运维·人工智能·学习
木木_王16 小时前
嵌入式Linux学习 | 数据结构(Day06)全解:线性表 + 栈队列 + 静态库 / 动态库(原理 + 代码 + 编译实战 + 易错点)
linux·数据结构·笔记·学习
-Springer-16 小时前
STM32 学习 —— 个人学习笔记11-2(SPI 通信外设 & 硬件 SPI 读写 W25Q64)
笔记·stm32·学习
许彰午16 小时前
我手写了一个 Java 内存数据库(四):索引引擎、SQL 解析与总结
java·数据库·sql
中屹指纹浏览器16 小时前
浏览器指纹内核级篡改技术实现与风险防御
经验分享·笔记
TO_ZRG17 小时前
Android Broadcast Receiver完全入门指南
java·后端·spring
Knight_AL17 小时前
使用 CyclicBarrier + 自定义线程池实现 SpringBoot 并行报表(完整性能对比)
java·spring boot·后端
人道领域17 小时前
【LeetCode刷题日记】347.前k个高频元素
java·数据结构·算法·leetcode
tjl521314_2117 小时前
02C++ 静态变量与链接性
java·jvm·c++
天天爱吃肉821817 小时前
笔记:同步电机调试时电角度校正方法说明
大数据·人工智能·笔记·功能测试·嵌入式硬件·汽车