OpenGL ES - glReadPixels

时间:2011-11-21 08:52:00

标签: android c++ ios opengl-es marmalade

我正在使用glReadPixels截取屏幕截图,以便在两张图片之间执行“交叉”效果。

在Marmalade SDK模拟器上,屏幕截图很好,“交叉”效果可以解决问题: enter image description here

然而,这是它在iOS和Android设备上的外观 - 已损坏: http://www.eikona.info/images/81269689420703803966.png

我总是将屏幕视为RGBA 1字节/通道,因为documentation says始终接受它。

以下是用于截屏的代码:

uint8* Gfx::ScreenshotBuffer(int& deviceWidth, int& deviceHeight, int& dataLength) {

    /// width/height
    deviceWidth = IwGxGetDeviceWidth();
    deviceHeight = IwGxGetDeviceHeight();
    int rowLength = deviceWidth * 4; /// data always returned by GL as RGBA, 1 byte/each

    dataLength = rowLength * deviceHeight;

    // set the target framebuffer to read
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    uint8* buffer = new uint8[dataLength];
    glReadPixels(0, 0, deviceWidth, deviceHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    return buffer;
}

void Gfx::ScreenshotImage(CIwImage* img, uint8*& pbuffer) {

    int deviceWidth, deviceHeight, dataLength;

    pbuffer = ScreenshotBuffer(deviceWidth, deviceHeight, dataLength);
    img->SetFormat(CIwImage::ABGR_8888);
    img->SetWidth(deviceWidth);
    img->SetHeight(deviceHeight);
    img->SetBuffers(pbuffer, dataLength, 0, 0);
}

6 个答案:

答案 0 :(得分:5)

这是一个驱动程序错误。就这么简单。

司机得知视频内存表面的间距错误。您可以在上面的行中清楚地看到这一点。此外,您在图像下部看到的垃圾是驱动程序认为存储图像的内存,但存在不同的数据。纹理/顶点数据可能。

抱歉,我知道无法解决这个问题。您可以通过不同的表面格式或启用/禁用多重采样获得更好的运气。

答案 1 :(得分:5)

最后,缺乏记忆力。 “new uint8 [dataLength];”从来没有返回一个存在的指针,因此整个过程都被破坏了。

TomA,你清理缓冲区的想法实际上帮助我解决了这个问题。谢谢。

答案 2 :(得分:3)

我不知道你正在使用的android或SDK,但是当我拍摄截图时,我必须使缓冲区的大小与下一个POT纹理相同,如下所示:

int x = NextPot((int)screenSize.x*retina);
int y = NextPot((int)screenSize.y*retina);

void *buffer = malloc( x * y * 4 );

glReadPixels(0,0,x,y,GL_RGBA,GL_UNSIGNED_BYTE,buffer);

函数NextPot只给我下一个POT大小,所以如果屏幕大小为320x480,则x,y为512x512。

也许你看到的是缓冲区的环绕,因为它期望更大的缓冲区大小?

此外,这可能是它在模拟器中工作而不是在设备上工作的原因,我的显卡没有POT大小限制,我得到类似(奇怪的外观)结果。

答案 3 :(得分:3)

我假设发生的事情是您尝试在所覆盖的窗口上使用glReadPixels。如果覆盖了视图区域,则glReadPixels的结果未定义。

请参阅How do I use glDrawPixels() and glReadPixels()?The Pixel Ownership Problem

如上所述here

  

解决方案是制作一个屏幕外缓冲区(FBO)并渲染到   FBO。

另一个选择是确保在使用glReadPixels时未覆盖窗口。

答案 4 :(得分:1)

我没有修复glReadPixels的解决方案。我的建议是你改变算法以避免需要从屏幕上读回数据。

看看this page。这些人在Flash中完成了翻页效果。一切都是二维的,只有阴影渐变才能实现幻觉。

我认为你可以使用类似的方法,但在3D方面要好一点。基本上你必须将效果分成三个部分:正面朝上的首页(云),底页(女孩)和首页的背面。你必须分别绘制每个部分。您可以在同一屏幕中轻松地将正面朝上的首页和底部页面绘制在一起,您只需要为每个绘图代码调用一个预设剪裁区域,该剪裁区域与顶部页面弯曲的分割线对齐。在绘制了顶部和背面部分后,可以在顶部绘制灰色背面部分,也与分割线对齐。

使用这种方法,你唯一丢失的是云图像开始弯曲时的一点变形,当然我的方法不会发生变形。希望这不会减少效果,我认为阴影对于产生深度效果更重要,并且会隐藏这种轻微的不一致。

答案 5 :(得分:1)

我正在使用glReadPixels在Android设备上获取我的安卓游戏的大纲,而没有任何问题。

我不确定您的问题是什么,需要更多信息。 让我们开始吧:

  1. 我建议您不要指定PixelStore格式。我担心你的对齐是1字节,你真的"使用它" /"知道它做了什么"?看起来你得到了你指定的内容 - 一个额外的字节(看你的图像,一直有一个额外的像素!)而不是完全打包的图像。所以试着删除它:

    glPixelStorei(GL_UNPACK_ALIGNMENT,1); glPixelStorei(GL_PACK_ALIGNMENT,1);

  2. 我不确定在C代码中,因为我只在java中工作,但这看起来可能有点:

    //宽度/高度 deviceWidth = IwGxGetDeviceWidth(); deviceHeight = IwGxGetDeviceHeight();

  3. 您是否正在获得设备尺寸?您应该使用OpenGL表面大小,如下所示:

    public void onSurfaceChanged(GL10 gl, int width, int height) {
    int surfaceWidth = width;
    int surfaceHeight = height;
    }
    
    1. 接下来你拍摄的图像是做什么的?你是否意识到你从opengl获得的内存块是RGBA,但所有非opengl图像操作都期望ARGB? 例如,在您的代码中,您希望alpha是第一位,而不是最后一位:

      img-> SetFormat(CIwImage :: ABGR_8888);

    2. 如果1,2和3没有帮助,您可能希望将捕获的屏幕保存到手机SD卡以便稍后检查。我有一个程序将opengl RGBA块转换为普通位图,以便在PC上进行检查。我可能会与你分享。