
osg::Camera::DrawCallback (抓取图片)
在OpenSceneGraph(OSG)三维渲染开发中,相机(Camera) 是场景渲染的核心入口,控制着三维场景到二维屏幕的投影、绘制流程等关键逻辑。
而osg::Camera::DrawCallback作为OSG提供的相机绘制阶段回调机制,是实现帧缓存读取、屏幕截图、渲染后处理、自定义绘制等高级功能的核心技术。
本文将结合OSG屏幕截图实战代码 ,深度解析osg::Camera::DrawCallback的继承关系、工作原理、使用方法,帮你彻底掌握这一OSG核心组件。
基础认知
1. 核心定义
osg::Camera::DrawCallback是OSG中专门用于监听相机渲染流程 的抽象回调类,它允许开发者在相机渲染的前置阶段(PreDraw) 和后置阶段(PostDraw) 插入自定义逻辑,无需修改OSG底层渲染源码。
简单来说:它是相机渲染的「钩子函数」,相机渲染到指定阶段时,会自动执行我们重写的回调逻辑。
2. 核心应用场景
结合本文截图代码,该回调最常用的场景包括:
- 屏幕截图/帧缓存读取(PostDraw阶段,渲染完成后读取像素)
- 渲染后处理(滤镜、灰度、模糊等效果)
- 自定义绘制(叠加UI、标记、辅助图形)
- 渲染状态监控/性能统计
类的继承关系(核心图谱)
osg::Camera::DrawCallback并非孤立存在,它遵循OSG的引用计数+继承架构,完整继承关系如下:
osg::Referenced (OSG所有智能指针管理类的基类)
↓ 继承
osg::Camera::DrawCallback (抽象回调基类)
↓ 继承
自定义回调类(如本文的 CaptureDrawCallback)
关键说明:
-
osg::Referenced
OSG所有需要自动内存管理的类的顶级基类,提供引用计数 功能,配合
osg::ref_ptr智能指针使用,避免内存泄漏。✅ 本文中
CaptureDrawCallback无需手动释放,由OSG自动管理生命周期。 -
osg::Camera::DrawCallback
纯虚抽象类,核心接口:
cppvirtual void operator()(const osg::Camera& camera) const = 0;这是必须重写的纯虚函数,自定义逻辑全部写在该函数内。
-
自定义子类(CaptureDrawCallback)
继承
DrawCallback,重写operator()实现具体功能(本文中为读取帧缓存、截图数据采集)。
相机渲染流程与回调执行时机
OSG相机的渲染分为三个核心阶段,回调的执行时机直接决定功能是否生效:
| 回调类型 | 执行时机 | 适用场景 | 本文使用情况 |
|---|---|---|---|
| PreDrawCallback | 相机开始渲染之前 | 渲染前修改状态、清空缓存 | 不适用 |
| DrawCallback | 相机渲染过程中 | 中间层自定义绘制 | 不适用 |
| PostDrawCallback | 相机渲染完成后 | 读取像素、截图、后处理 | ✅ 核心使用 |
关键结论:
屏幕截图必须使用 PostDrawCallback !
因为只有渲染完成后,GPU帧缓存才有完整的场景像素数据,此时调用readPixels才能拿到有效画面,否则会出现黑屏、残缺等问题。
结合截图代码:DrawCallback 实战解析
全部代码:
c
#include <osgViewer/Viewer>
#include <osgViewer/GraphicsWindow>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Camera>
#include <osg/Image>
#include <osg/BufferObject>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <iostream>
// 定义全局变量,用于在回调和事件处理器之间传递图像数据
osg::ref_ptr<osg::Image> image_c = new osg::Image();
/***********************************************************************
* 相机绘制回调:在绘制完成后读取帧缓存
* 继承自 osg::Camera::DrawCallback,在相机绘制流程中被调用
***********************************************************************/
struct CaptureDrawCallback : public osg::Camera::DrawCallback
{
public:
// 构造函数:接收外部传入的osg::Image对象
CaptureDrawCallback(osg::ref_ptr<osg::Image> image)
{
_image = image;
}
~CaptureDrawCallback() {}
// 重载operator():绘制回调的核心执行函数
virtual void operator()(const osg::Camera& camera) const
{
// 1. 获取窗口系统接口(跨平台抽象层,适配不同操作系统的窗口API)
osg::ref_ptr<osg::GraphicsContext::WindowingSystemInterface> wsi =
osg::GraphicsContext::getWindowingSystemInterface();
unsigned int width, height;
// 2. 获取主屏幕分辨率(也可以根据窗口句柄获取窗口大小)
wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);
// 3. 为Image分配内存:宽、高、像素格式、数据类型
_image->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
// 4. 读取帧缓存数据:从(0,0)开始,读取整个窗口的RGB像素数据
_image->readPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE);
}
// 成员变量:存储要写入数据的Image对象
osg::ref_ptr<osg::Image> _image;
};
/***********************************************************************
* 事件处理器:处理键盘输入,触发截图操作
* 继承自 osgGA::GUIEventHandler,用于捕获用户交互事件
***********************************************************************/
class ImageHandler : public osgGA::GUIEventHandler
{
public:
ImageHandler() {}
~ImageHandler() {}
// 重载handle()函数:事件处理的核心逻辑
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
// 1. 将ActionAdapter转换为Viewer指针,获取Viewer实例
osg::ref_ptr<osgViewer::Viewer> viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
if (viewer == nullptr)
return false;
// 静态变量:记录截图序号,实现连续截图(ScreenShot000.bmp, 001.bmp...)
static int _screenCaptureSequence = 0;
// 2. 判断事件类型:键盘按下事件
switch (ea.getEventType())
{
case osgGA::GUIEventAdapter::KEYDOWN:
{
// 按下'c'或'C'键时触发截图
if (ea.getKey() == 'c' || ea.getKey() == 'C')
{
char filename[128];
// 格式化文件名,生成带序号的BMP文件
sprintf(filename, "ScreenShot%04d.bmp", _screenCaptureSequence++);
// 写入文件:使用osgDB工具类,将Image数据保存为BMP格式
osgDB::writeImageFile(*(image_c.get()), filename);
}
break;
}
default:
// 其他事件不处理,返回false表示事件未被消费
return false;
}
// 返回true表示事件已被处理
return true;
}
};
/***********************************************************************
* 主函数:程序入口
***********************************************************************/
int main()
{
// 1. 创建Viewer实例(OSG渲染主循环控制器)
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
// 2. 创建场景根节点
osg::ref_ptr<osg::Group> root = new osg::Group();
// 3. 读取外部模型(示例为cow.osg,可替换为其他OSG支持的模型格式)
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
if (node == nullptr)
{
std::cerr << "Error: 无法读取模型文件 cow.osg,请确保文件在程序目录下!" << std::endl;
return -1;
}
root->addChild(node.get());
// 4. 场景优化:使用OSG内置优化器,提升渲染性能
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
// 5. 设置相机绘制回调:在绘制完成后读取帧缓存
viewer->getCamera()->setPostDrawCallback(new CaptureDrawCallback(image_c.get()));
// 6. 设置场景数据:将根节点传入Viewer
viewer->setSceneData(root.get());
// 7. 添加事件处理器:注册截图按键事件
viewer->addEventHandler(new ImageHandler());
// 8. 初始化窗口并启动渲染主循环
viewer->realize();
viewer->run();
return 0;
}
1. 自定义回调类定义(继承与重写)
cpp
// 1. 继承 osg::Camera::DrawCallback
struct CaptureDrawCallback : public osg::Camera::DrawCallback
{
public:
// 构造函数:接收图像对象,用于存储截图数据
CaptureDrawCallback(osg::ref_ptr<osg::Image> image)
{
_image = image;
}
// 2. 重写纯虚函数 operator() → 核心逻辑入口
virtual void operator()(const osg::Camera& camera) const
{
// 获取屏幕/窗口尺寸
unsigned int width, height;
auto wsi = osg::GraphicsContext::getWindowingSystemInterface();
wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);
// 分配图像内存
_image->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
// 3. 核心功能:读取GPU帧缓存像素数据(截图核心)
_image->readPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE);
}
// 成员变量:存储像素数据
osg::ref_ptr<osg::Image> _image;
};
代码关键点:
- 严格继承
osg::Camera::DrawCallback,遵循OSG继承规范; - 重写
operator()纯虚函数,这是回调的唯一入口; - 函数内实现帧缓存读取 ,将GPU数据同步到CPU的
osg::Image中。
2. 回调注册到相机
在main函数中,将自定义回调绑定到相机的PostDraw阶段:
cpp
// 注册后置绘制回调 → 渲染完成后自动执行
viewer->getCamera()->setPostDrawCallback(new CaptureDrawCallback(image_c.get()));
这一步是回调生效的关键 :告诉OSG相机,渲染完成后执行CaptureDrawCallback的逻辑。
3. 配合事件处理器完成截图
DrawCallback负责采集数据 ,键盘事件处理器负责保存数据:
cpp
// 按下c键,将Image数据写入文件
osgDB::writeImageFile(*(image_c.get()), filename);
使用核心规范
-
必须继承+重写
自定义类必须公有继承
osg::Camera::DrawCallback,且必须实现operator()纯虚函数。 -
const 约束不可忽略
operator()函数是const修饰的,函数内不能修改类的非mutable成员变量。 -
执行时机选对
- 截图/后处理 →
setPostDrawCallback - 渲染前初始化 →
setPreDrawCallback
- 截图/后处理 →
-
内存自动管理
基于
osg::Referenced,回调对象用new创建即可,无需手动delete。
总结
osg::Camera::DrawCallback是OSG相机渲染的核心扩展接口 ,它通过继承抽象类+回调钩子的设计,让开发者可以灵活介入相机渲染流程。
结合本文截图代码,我们可以总结其核心价值:
- 继承关系 :
osg::Referenced→osg::Camera::DrawCallback→ 自定义回调; - 核心作用:在相机渲染的指定阶段执行自定义逻辑;
- 实战用法:PostDraw回调读取帧缓存 + 事件处理器保存图像,实现稳定截图;
- 设计优势:无侵入式扩展,不修改OSG源码,满足个性化渲染需求。
