【问题标题】:OpenGL ES 2d rendering into imageOpenGL ES 2d 渲染成图像
【发布时间】:2012-05-14 09:09:46
【问题描述】:

我需要在 iOS 上编写 OpenGL ES 二维渲染器。它应该将一些图元(如线条和多边形)绘制到二维图像中(它将渲染矢量图)。在该任务中,哪种方式最适合从 OpenGL 上下文中获取图像?我的意思是,我应该将这些图元渲染成纹理,然后从中获取图像,还是什么?此外,如果有人提供看起来像我需要的东西(2d GL 渲染到图像)的示例或教程,那将是很棒的。提前致谢!

【问题讨论】:

    标签: iphone ios image opengl-es rendering


    【解决方案1】:

    如果您需要渲染 OpenGL ES 2-D 场景,然后提取该场景的图像以在 OpenGL ES 之外使用,您有两个主要选择。

    第一种是简单地渲染您的场景并使用glReadPixels() 获取场景的 RGBA 数据并将其放置在一个字节数组中,如下所示:

    GLubyte *rawImagePixels = (GLubyte *)malloc(totalBytesForImage);
    glReadPixels(0, 0, (int)currentFBOSize.width, (int)currentFBOSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
    // Do something with the image
    free(rawImagePixels);
    

    第二种更快的方法是将场景渲染到纹理支持的帧缓冲区对象 (FBO),其中纹理由 iOS 5.0 的纹理缓存提供。我在this answer 中描述了这种方法,尽管我没有在那里展示原始数据访问的代码。

    您执行以下操作来设置纹理缓存并绑定 FBO 纹理:

        CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &rawDataTextureCache);
        if (err) 
        {
            NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d");
        }
    
        // Code originally sourced from http://allmybrain.com/2011/12/08/rendering-to-a-texture-with-ios-5-texture-cache-api/
    
        CFDictionaryRef empty; // empty value for attr value.
        CFMutableDictionaryRef attrs;
        empty = CFDictionaryCreate(kCFAllocatorDefault, // our empty IOSurface properties dictionary
                                   NULL,
                                   NULL,
                                   0,
                                   &kCFTypeDictionaryKeyCallBacks,
                                   &kCFTypeDictionaryValueCallBacks);
        attrs = CFDictionaryCreateMutable(kCFAllocatorDefault,
                                          1,
                                          &kCFTypeDictionaryKeyCallBacks,
                                          &kCFTypeDictionaryValueCallBacks);
    
        CFDictionarySetValue(attrs,
                             kCVPixelBufferIOSurfacePropertiesKey,
                             empty);
    
        //CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferInput pixelBufferPool], &renderTarget);
    
        CVPixelBufferCreate(kCFAllocatorDefault, 
                            (int)imageSize.width, 
                            (int)imageSize.height,
                            kCVPixelFormatType_32BGRA,
                            attrs,
                            &renderTarget);
    
        CVOpenGLESTextureRef renderTexture;
        CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault,
                                                      rawDataTextureCache, renderTarget,
                                                      NULL, // texture attributes
                                                      GL_TEXTURE_2D,
                                                      GL_RGBA, // opengl format
                                                      (int)imageSize.width, 
                                                      (int)imageSize.height,
                                                      GL_BGRA, // native iOS format
                                                      GL_UNSIGNED_BYTE,
                                                      0,
                                                      &renderTexture);
        CFRelease(attrs);
        CFRelease(empty);
        glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture));
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);
    

    然后您可以直接从支持此纹理的字节中读取(BGRA 格式,而不是 glReadPixels() 的 RGBA),使用类似:

        CVPixelBufferLockBaseAddress(renderTarget, 0);
        _rawBytesForImage = (GLubyte *)CVPixelBufferGetBaseAddress(renderTarget);
        // Do something with the bytes
        CVPixelBufferUnlockBaseAddress(renderTarget, 0);
    

    但是,如果您只想在 OpenGL ES 中重复使用您的图像,您只需将场景渲染到支持纹理的 FBO,然后在您的第二级渲染中使用该纹理。

    我展示了一个渲染到纹理的示例,然后在我的开源 GPUImage 框架内的 CubeExample 示例应用程序中对其执行一些处理,如果您想看到它的实际效果。

    【讨论】:

    • @medvedNick - glReadPixels() 适用于所有操作系统版本,但纹理缓存仅在 iOS 5.0 中首次引入。它们的速度要快得多,但仅限于 5.0。不过,您可以进行运行时检查,然后在较旧的操作系统版本上回退到较慢的 glReadPixels()。再次检查我的 GPUImage 代码,因为我在其中执行此操作。
    • 纹理缓存是 5.0 及更高版本,但它们不是仅在某些设备上速度很快吗?例如在 4S/iPad2 上速度很快,在其他任何地方都相当于 glReadPixels/glTexImage2D
    • @RhythmicFistman - 如果使用得当,在我的基准测试中运行 5.0 的每台设备上,它们都比 glReadPixels() 快。您可以避免昂贵的颜色转换并直接访问内存中的原始像素数据以获取缓存纹理。我在 iPad 1 和 iPhone 4 上看到了显着的好处(我没有运行 5.0 的 3G S,但我也听说了那里的好东西)。
    • 没有意外的颜色转换绝对是 API 的好处。我尝试使用纹理缓存作为视频播放器中 glTexImage2D 的替代品,但令人困惑的是,结果与传统实现无法区分,不确定出了什么问题。但是我很困惑-如果您可以直接访问纹理的内存,那么您肯定有硬件支持吗?无论如何,我计划在 v1.1 中重新审视它们,那里一定会有性能提升。
    • @RhythmicFistman - 对于上传,您通常将 BGRA 指定为颜色格式,因此您不会注意到两种方法之间的图像颜色差异。使用 AVAssetReader,我仍然注意到缓存的显着加速,尽管不如处理实时摄像头馈送时那么好。在 iOS 设备上,GPU 与系统共享内存,我相信 Apple 只是使用私有(在 iOS 上)IOSurface 框架来访问它。所有支持 5.0 的设备都可以使用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 2010-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多