我有一个问题,有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图像附加到视图中。我不确定这个问题。请帮忙。
答案 0 :(得分: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();
答案 1 :(得分:1)
这个问题已经解决了,但我想说明,Idoogy的答案实际上是危险的,对大多数用例来说都是一个糟糕的选择。
您可以使用现有委托方法来完成同样的事情,而不是将CAEAGLLayer子类化并创建新的委托对象。例如:
- (void) drawLayer:(CALayer *) layer inContext:(CGContextRef)ctx;
是在基于GL的视图中实现的一种很好的方法。您可以使用glReadPixels以与建议相同的方式实现它:只需确保将视图上的Retained-Backing属性设置为YES,这样您就可以随时调用上述方法,而不必担心它已被无效展示的展示。
使用现有的UIView / CALayer委托关系对CAEAGL图层进行子类化:在大多数情况下,在自定义图层上设置委托对象将导致您的UIView被排除在视图层次结构之外。因此,代码如:
customLayerView = [[CustomLayerView alloc] initWithFrame:someFrame];
[someSuperview addSubview:customLayerView];
将导致一个奇怪的,单向的superview-subview关系,因为UIView所依赖的委托方法将不会被实现。 (但您的超级视图仍将具有自定义视图中的子图层)。
因此,只需实现一些委托方法,而不是继承CAEAGLLayer。 Apple在此为您提供:https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871
一切顺利,
萨姆
答案 2 :(得分:0)