6

我有一个问题,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 图像。我不确定这个问题。请帮忙。

4

3 回答 3

6

我有一个类似的问题,并找到了一个更优雅的解决方案。基本上,您将 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();
于 2013-04-03T01:39:20.730 回答
1

这个问题已经解决了,但我想指出 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,不如实现一些委托方法。Apple 在这里为您列出:https ://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871

祝一切顺利,

山姆

于 2013-10-28T18:58:31.410 回答
0

我认为http://developer.apple.com/library/ios/#qa/qa1704/_index.html提供了你想要的。

于 2012-09-24T12:37:00.080 回答