【问题标题】:Issue with "renderincontext" with opengl views带有opengl视图的“renderincontext”问题
【发布时间】:2013-10-29 02:58:31
【问题描述】:

我有一个问题,openGL 视图。我有两个 openGL 视图。第二个视图作为子视图添加到主视图。这两个 opengl 视图是在两个不同的 opengl 上下文中绘制的。我需要使用两个 opengl 视图来捕获屏幕。

问题是,如果我尝试在如下上下文中渲染一个 CAEAGLLayer:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 1*(self.frame.size.width*0.5), 1*(self.frame.size.height*0.5));
CGContextScaleCTM(context, 3, 3);
CGContextTranslateCTM(context, abcd, abcd);

CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.myOwnView.layer;
[eaglLayer renderInContext:context];

它不起作用。如果我看到上下文(给定输出为图像),opengl 层中的内容丢失。但是我在输出图像中找到了附加到视图的工具栏和 2d 图像。我不确定这个问题。请帮忙。

【问题讨论】:

    标签: objective-c ios


    【解决方案1】:

    我遇到了类似的问题,并找到了一个更优雅的解决方案。基本上,您将 CAEAGLLayer 子类化,并添加您自己的 renderInContext 实现,它只是要求 OpenGL 视图使用 glReadPixels 呈现内容。美妙之处在于,现在您可以在层次结构中的任何层上调用 renderInContext,结果是一个完整的、外观完美的屏幕截图,其中包含您的 OpenGL 视图!

    我们在子类 CAEAGLLayer 中的 renderInContext 是:

    - (void)renderInContext:(CGContextRef)ctx
    {
        [super renderInContext: ctx];
        [self.delegate renderInContext: ctx];
    }
    

    然后,在 OpenGL 视图中,我们替换 layerClass 以便它返回我们的子类,而不是普通的 CAEAGLLayer:

    + (Class)layerClass
    {
        return [MyCAEAGLLayer class];
    }
    

    我们在视图中添加一个方法来实际将视图的内容渲染到上下文中。请注意,此代码必须在您的 GL 视图被渲染之后,但在您调用 presentRenderbuffer 之前运行,以便渲染缓冲区将包含您的帧。否则,生成的图像很可能是空的(您可能会在此特定问题上看到设备和模拟器之间的不同行为)。

    - (void) renderInContext: (CGContextRef) context
    {
        GLint backingWidth, backingHeight;
    
        // Bind the color renderbuffer used to render the OpenGL ES view
        // If your application only creates a single color renderbuffer which is already bound at this point,
        // this call is redundant, but it is needed if you're dealing with multiple renderbuffers.
        // Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class.
        glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    
        // Get the size of the backing CAEAGLLayer
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
    
        NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
        NSInteger dataLength = width * height * 4;
        GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
    
        // Read pixel data from the framebuffer
        glPixelStorei(GL_PACK_ALIGNMENT, 4);
        glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
    
        // Create a CGImage with the pixel data
        // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
        // otherwise, use kCGImageAlphaPremultipliedLast
        CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
                                        ref, NULL, true, kCGRenderingIntentDefault);
    
        CGFloat scale = self.contentScaleFactor;
        NSInteger widthInPoints, heightInPoints;
        widthInPoints = width / scale;
        heightInPoints = height / scale;
    
        // UIKit coordinate system is upside down to GL/Quartz coordinate system
        // Flip the CGImage by rendering it to the flipped bitmap context
        // The size of the destination area is measured in POINTS
        CGContextSetBlendMode(context, kCGBlendModeCopy);
        CGContextDrawImage(context, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
    
        // Clean up
        free(data);
        CFRelease(ref);
        CFRelease(colorspace);
        CGImageRelease(iref);
    }
    

    最后,为了截取屏幕截图,您可以按照通常的方式使用 renderInContext。当然,美妙之处在于您不需要直接抓取 OpenGL 视图。您可以抓取 OpenGL 视图的其中一个超级视图,并获得包含 OpenGL 视图及其旁边或顶部的任何其他内容的合成屏幕截图:

    UIGraphicsBeginImageContextWithOptions(superviewToGrab.bounds.size, YES, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [superviewToGrab.layer renderInContext: context]; // This recursively calls renderInContext on all the sublayers, including your OpenGL layer(s)
    CGImageRef screenShot = UIGraphicsGetImageFromCurrentImageContext().CGImage;
    UIGraphicsEndImageContext();
    

    【讨论】:

    • 我正在尝试您的方法,但 glReadPixels 似乎没有将任何数据复制到我的缓冲区(示例中的“数据”变量)。关于为什么会出现这种情况的任何想法?我的设置是一个常规的 UIView,其支持层是 CAEAGLLayer。然后,我添加一个带有图像的 UIImageView 作为 GL 支持的视图的子项。然后我尝试抓取 renderBuffer 的内容,我的缓冲区仍然是全 0,就像我用 calloc 初始化它时一样。我还尝试将图层设置为渲染缓冲区存储,但没有。如果我执行常规的 renderInContext,我确实会将内容作为图像返回。
    【解决方案2】:

    这个问题已经解决了,但我想指出,Idoogy 的答案实际上是危险的,对于大多数用例来说都是一个糟糕的选择。

    您可以使用现有的委托方法来完成完全相同的事情,而不是继承 CAEAGLLayer 并创建一个新的委托对象。例如:

    - (void) drawLayer:(CALayer *) layer inContext:(CGContextRef)ctx;
    

    是在基于 GL 的视图中实现的好方法。您可以使用 glReadPixels 以与他建议的方式相同的方式实现它:只需确保将视图上的 Retained-Backing 属性设置为 YES,这样您就可以随时调用上述方法,而不必担心它已被无效演示文稿。

    子类化 CAEAGL 层与现有的 UIView / CALayer 委托关系混淆:在大多数情况下,在您的自定义层上设置委托对象将导致您的 UIView 被排除在视图层次结构之外。因此,代码如下:

    customLayerView = [[CustomLayerView alloc] initWithFrame:someFrame];
    [someSuperview addSubview:customLayerView];
    

    将导致一种奇怪的、单向的超级视图-子视图关系,因为 UIView 所依赖的委托方法不会被实现。 (不过,您的超级视图仍将拥有自定义视图中的子图层)。

    因此,与其继承 CAEAGLLayer,不如实现一些委托方法。苹果在这里为你准备好了:https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871

    一切顺利,

    山姆

    【讨论】:

    • 我将发布一个迟来的回应:对 CAEAGLLayer 进行子类化是非常安全的,并且会导致 100% 相同的行为。那是因为我们将 CAEAGLLayer 子类化的方式不是通过使用我们自己的类手动初始化它,而是通过将该类作为 UIView 的 layerClass 提供(如果您阅读了我的回答,您会看到我推荐的方法) .这确保了初始化是由系统完成的,因此一切都与没有子类、委托和所有内容的情况完全相同。
    【解决方案3】:

    【讨论】:

    • 感谢您的回复。我有两个彼此重叠的opengl视图(纹理)。我需要捕获两个视图的结果。我不太确定,如果指针能帮助我得到我想要的。
    • 这两个纹理是在两个不同的opengl上下文中绘制的。
    • 只需为每个上下文执行一次代码,然后根据需要将两张图像合成为一张。
    猜你喜欢
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    相关资源
    最近更新 更多