OSG底层从Texture读取Image实现:readImageFromCurrentTexture

OSG渲染到纹理:Image与Texture2D深度解析


由于 osg::Image::readImageFromCurrentTexture 属于底层 OpenGL 操作的封装,它要求您在使用前手动绑定目标纹理。

下面是使用 readImageFromCurrentTexture 方法读取 colordepthTexture 数据的完整实现细节,包括所需的头文件、回调类定义,以及核心逻辑。


📝 完整实现代码:使用 readImageFromCurrentTexture

我们将修改您现有的 SaveImageCallback 类,以实现手动绑定和读取。

1. 所需头文件

除了常用的 OSG 头文件外,您需要引入 osg/Stateosg/GLExtensions 来处理 OpenGL 状态和纹理绑定。

c++ 复制代码
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Camera>
#include <osg/RenderInfo>
#include <osg/State>          // 用于获取 OpenGL 状态
#include <osg/GLExtensions>   // 包含 OpenGL 上下文和绑定函数
#include <osgDB/WriteFile>    // 用于保存文件
#include <iostream>
#include <string>

// 假设 Config 结构体和 SaveRequestHandler 已经定义
// struct Config {}; 
// class SaveRequestHandler : public osgGA::GUIEventHandler {...}; 

2. SaveImageCallback 类定义和实现

我们将使用 readImageFromCurrentTexture 方法替换掉之前的 copyImage 调用。

c++ 复制代码
class SaveImageCallback : public osg::Camera::PostDrawCallback
{
private:
    osg::ref_ptr<osg::Texture2D> _sourceTexture;
    osg::ref_ptr<osg::Image> _targetImage;
    bool _saveRequested = false;
    // 可以添加 Config 成员,这里为简化省略

public:
    // 构造函数:接受源纹理和目标Image
    SaveImageCallback(osg::Texture2D* sourceTexture, osg::Image* targetImage)
        : _sourceTexture(sourceTexture), _targetImage(targetImage) {}

    void requestSave() {
        _saveRequested = true;
    }

    void operator()(osg::RenderInfo& renderInfo) const override
    {
        if (!_saveRequested) return;

        osg::State* state = renderInfo.getState();
        unsigned int contextID = renderInfo.getContextID();
        
        if (!_targetImage->valid() || !_sourceTexture->valid()) {
            std::cerr << "错误: 目标 Image 或源 Texture 无效,无法读取。" << std::endl;
            _saveRequested = false;
            return;
        }

        // --- 核心逻辑:手动绑定纹理并读取 ---
        
        // 1. 获取 Texture2D 的底层 OpenGL ID
        GLuint textureID = _sourceTexture->getTextureObject(contextID)->id();

        // 2. 绑定纹理到 GL_TEXTURE_2D 目标
        // 注意:这里需要确保 GL 函数被正确调用。在 OSG 环境中,可以直接使用 state->apply(Attribute) 或 gles(contextID) 来管理。
        // 最直接的方式是调用 OpenGL 扩展函数,但为了跨平台和 OSG 兼容性,我们使用 OSG 封装的方法。

        // 绑定纹理:这确保了 OpenGL 知道我们要从哪个纹理读取数据
        state->get
(contextID)->glActiveTexture(GL_TEXTURE0 + _sourceTexture->getTextureUnit()); // 激活纹理单元
        state->getExtensions(contextID)->glBindTexture(GL_TEXTURE_2D, textureID); // 绑定纹理 ID

        std::cout << "DEBUG: 成功绑定 Texture ID " << textureID << " 到 GL_TEXTURE_2D。" << std::endl;

        // 3. 调用 readImageFromCurrentTexture
        // 参数:
        // contextID: 渲染上下文 ID
        // copyMipMapsIfAvailable: false (我们只关心 Level 0 的高程数据)
        // type: GL_FLOAT (因为 colordepthTexture 的 SourceType 是 GL_FLOAT)
        // face: 0 (不是立方体贴图)
        
        // Image 在调用前必须分配好内存,这已在 main 函数中完成 (GL_R32F, GL_FLOAT)
        _targetImage->readImageFromCurrentTexture(
            contextID, 
            false,           // 不拷贝 MipMaps
            GL_FLOAT,        // 期望读取的数据类型是 GL_FLOAT
            0                // Texture Face 0 (默认)
        );
        
        // 4. (可选但推荐) 恢复状态:解除纹理绑定
        state->getExtensions(contextID)->glBindTexture(GL_TEXTURE_2D, 0); 
        // ------------------------------------

        
        // 5. 保存数据到文件
        std::string filename = "ActualZ_HeightMap_ManualRead.tiff";
        
        // 检查 Image 的格式是否正确 (应为 GL_RED, GL_FLOAT)
        if (_targetImage->getDataType() == GL_FLOAT && _targetImage->getPixelFormat() == GL_RED)
        {
             std::cout << "INFO: Image 数据格式确认:GL_RED/GL_FLOAT。" << std::endl;

             // 尝试保存为 TIFF 或其他支持浮点数的格式
             if (osgDB::writeImageFile(*_targetImage, filename)) 
             {
                 std::cout << "✅ 实际 Z 高度图已保存为: " << filename << std::endl;
             } 
             else 
             {
                 std::cerr << "❌ 保存失败: OSG可能不支持直接保存 GL_FLOAT TIFF。请考虑手动写入二进制文件。" << std::endl;
                 // 如果需要,可以在这里添加手动写入浮点数数组的逻辑
             }
        }
        else
        {
            std::cerr << "❌ 警告: readImageFromCurrentTexture 后的 Image 格式不匹配期望。" << std::endl;
        }


        _saveRequested = false; 
    }
};

3. 主程序调用修改

在主程序中,您只需要确保回调函数接收的参数顺序和类型与新的 SaveImageCallback 构造函数匹配。

原代码:

c++ 复制代码
osg::ref_ptr<SaveImageCallback> saveCallback = new SaveImageCallback(
    // 传入最终的高程结果 Image
    colordepthTexture.get(), elevationImage.get(), NULL,config // 这里的参数列表需要调整
);

修改后的(假设您只传入了 Texture 和 Image):

c++ 复制代码
// 调整为新的构造函数顺序 (Texture, Image)
osg::ref_ptr<SaveImageCallback> saveCallback = new SaveImageCallback(
    colordepthTexture.get(), // Source Texture
    elevationImage.get()     // Target Image
);

为什么 copyImage 更常用?

使用 readImageFromCurrentTexture 需要您手动处理 OpenGL 状态(激活纹理单元、绑定纹理 ID),这使得代码与底层的 ContextIDGLExtensions 紧密耦合,增加了出错的可能性。

osg::Texture2D::copyImage(*image, ...) 内部已经包含了上述所有复杂的状态管理和底层调用,是 OSG 推荐和更健壮的纹理读回方式。

您提供的代码使用了 osg::Texture2D (colordepthTexture) 作为 RTT 目标,并且您希望使用 osg::Image::readImageFromCurrentTexture 方法从这个纹理中读取数据。

要使用 readImageFromCurrentTexture,您不能直接在 SaveImageCallback 中调用它,因为该方法需要先在 GPU 渲染上下文中显式地绑定 colordepthTexture

由于 osg::Texture::copyImage() 方法已经为您封装了所有这些底层细节(绑定纹理、调用 glGetTexImage 等),强烈建议您优先使用 colordepthTexture->copyImage(...)


替代方案:使用 readImageFromCurrentTexture(手动实现)

如果您坚持使用 osg::Image::readImageFromCurrentTexture,您必须手动处理纹理的绑定,这使得代码更复杂且容易出错。

SaveImageCallback::operator() 中实现此逻辑需要以下步骤:

  1. 获取当前 OpenGL 状态。
  2. 在当前上下文中使用 _sourceTexture->apply(state) 显式绑定纹理。
  3. 调用 _targetImage->readImageFromCurrentTexture(...)
  4. 解除纹理绑定(可选,但推荐)。

修改后的 SaveImageCallback 逻辑如下:

c++ 复制代码
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Camera>
#include <osg/RenderInfo>
#include <osg/State> // 需要 State 头文件

// ... (SaveImageCallback 结构与构造函数保持不变) ...

    void operator()(osg::RenderInfo& renderInfo) const override
    {
        if (!_saveRequested) return;

        osg::State* state = renderInfo.getState();
        unsigned int contextID = renderInfo.getContextID();

        // 1. **显式绑定纹理** 到 GL_TEXTURE_2D 目标
        // (这一步是使用 readImageFromCurrentTexture 的关键)
        _sourceTexture->apply(*state); // 自动绑定到 GL_TEXTURE_2D

        // 2. 调用 readImageFromCurrentTexture
        // 参数设置:
        // contextID: 渲染上下文 ID
        // copyMipMapsIfAvailable: false (通常 RTT 只需要 Level 0)
        // type: GL_FLOAT (匹配 colordepthTexture 的 SourceType)
        // face: 0 (不是 Cube Map)
        _targetImage->readImageFromCurrentTexture(
            contextID, 
            false,           // 不拷贝 MipMaps
            GL_FLOAT,        // 期望读取的数据类型是 GL_FLOAT
            0                // Texture Face 0
        );

        // **注意:** readImageFromCurrentTexture 内部会根据 Texture 的 InternalFormat (GL_R32F) 
        // 自动设置正确的 GL_RED 格式来调用 glGetTexImage。

        // 3. (可选) 解除纹理绑定或恢复状态
        // osg::Texture::unapplyTexture(state, GL_TEXTURE_2D);

        // **保存数据到文件...** (与前一方案相同)

        _saveRequested = false; 
    }
// ...

总结:

虽然 readImageFromCurrentTexture 可以工作,但由于您的 colordepthTexture 是一个 osg::Texture2D 实例,使用 _sourceTexture->copyImage(...) 是最简单、最安全、最推荐的方法。它避免了您手动管理 OpenGL 纹理绑定的复杂性。

相关推荐
!chen2 小时前
CPP 学习笔记 语法总结
c++·笔记·学习
杨筱毅2 小时前
【穿越Effective C++】条款17:以独立语句将newed对象置入智能指针——异常安全的智能指针初始化
c++·effective c++
Charles_go2 小时前
C#8、有哪些访问修饰符
java·前端·c#
慧一居士2 小时前
Vue中 class 和 style 属性的区别对比
前端·vue.js
moiumxf0278q3 小时前
C++中智能指针是如何工作的?
java·jvm·c++
似水এ᭄往昔3 小时前
【C++】--模板进阶
开发语言·c++
oil欧哟3 小时前
文心 5.0 来了,百度大模型的破局之战
前端·人工智能·百度·prompt
东华帝君3 小时前
react 切片 和 优先级调度
前端
AA陈超3 小时前
虚幻引擎5 GAS开发俯视角RPG游戏 P07-11 实现自动运行
c++·游戏·ue5·游戏引擎·虚幻