OpenGL ES 读取纹理数据到CPU地址,用于保存或其他计算等

一、问题描述

因为纹理数据是在GPU上的,CPU直接保存 glTexImage2D() 中的地址是没有效果的,我们需要把纹理的数据从GPU读取到CPU上,才能正常使用。

二、使用 glReadPixels()

1. 介绍

glReadPixels() 是一个同步读取操作,这个函数其实是直接用于从帧缓冲区中读取像素的颜色值,以获取渲染后的图像数据进行后续处理、保存到文件或其他用途。

所以我们可以先把纹理绑定到一个FBO(帧缓冲对象)上,然后再通过 glReadPixels() 读取纹理的值。

2. 函数定义

void glReadPixels (
        GLint x,        // 左上角x坐标
        GLint y,        // 左上角y坐标
        GLsizei width,  // 读取区域的宽度
        GLsizei height, // 读取区域的高度
        GLenum format,  // 像素数据的格式,例如 GL_RGBA
        GLenum type,    // 数据类型,例如 GL_UNSIGNED_BYTE
        void *pixels)   // 接收像素数据的缓冲区

3. 示例

void textureToBuffer(int textureId, int x, int y, int width, int height, unsigned char *buffer) {
    // 创建FBO
    GLuint fbo[1];
    glGenFramebuffers(1, fbo);
    // 绑定
    glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
    // 读取数据
    glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);  // 这里的format和type需要和纹理一致
    // 解绑和释放
    glBindFramebuffer(GL_FRAMEBUFFER, 0);  // unbind
    glDeleteFramebuffers(1, fbo);
}

如上只是一个简单的调用示例,实际场景中频繁的 glGenFramebuffers() 和 glDeleteFramebuffers() 也是有很大开销的,需要尽量避免。

三、使用像素缓冲对象(Pixel Buffer Object, PBO)

1. 介绍

PBO 是一种异步读取像素数据的方式,可以提高性能。当使用 glBindBuffer() 将一个非零的缓冲区对象绑定到 GL_PIXEL_PACK_BUFFER 时,glReadPixels() 将立即返回,并且启动DMA传输,从帧缓冲区读取像素,并将数据写入到像素缓冲对象(PBO)。

2. 示例

// 创建和绑定PBO
GLuint fbo[1];
glGenFramebuffers(1, fbo);
GLuint pbo[1];
glGenBuffers(1, pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]);
glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 4, nullptr, GL_STREAM_READ);

// 绑定帧缓冲对象
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
// 指定FBO的颜色附着为textureId纹理
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
// 读取纹理数据到PBO(异步操作)
// 请注意,这里传递的 nullptr 表示将数据读取到PBO中,而不是传递给CPU内存。
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// 映射PBO到CPU地址空间
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]);
void *pixels = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width * height * 4, GL_MAP_READ_BIT);
// 映射得到的地址是固定的,在CPU上处理数据...

// 解除PBO映射
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);  // unbind

// 释放
glDeleteBuffers(1, pbo);
glDeleteFramebuffers(1, fbo);

同样,创建和释放的操作也尽量避免反复执行,但绑定和解绑是需要每次执行的。由于PBO是异步的,性能通常更好,但代码更复杂。通常我们需要多个PBO联合使用,在CPU等待从一个PBO传输时,可以处理之前从另一个PBO传输的数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值