Tôi gặp sự cố tương tự và tìm thấy giải pháp thanh lịch hơn nhiều. Về cơ bản, bạn phân lớp CAEAGLLayer và thêm triển khai renderInContext của riêng bạn mà chỉ yêu cầu khung nhìn OpenGL để hiển thị nội dung bằng cách sử dụng glReadPixels. Cái đẹp là bây giờ bạn có thể gọi renderInContext trên bất kỳ lớp nào trong cấu trúc phân cấp, và kết quả là một ảnh chụp màn hình hoàn chỉnh, hoàn hảo, bao gồm cả khung nhìn OpenGL của bạn!
renderInContext của chúng tôi trong CAEAGLLayer subclassed là:
- (void)renderInContext:(CGContextRef)ctx
{
[super renderInContext: ctx];
[self.delegate renderInContext: ctx];
}
Sau đó, trong giao diện OpenGL chúng ta thay thế layerClass để nó trả về lớp con của chúng tôi thay vì vani CAEAGLLayer đồng bằng:
+ (Class)layerClass
{
return [MyCAEAGLLayer class];
}
Chúng tôi thêm một phương thức trong chế độ xem để thực sự hiển thị nội dung của chế độ xem vào ngữ cảnh. Lưu ý rằng mã này PHẢI chạy sau khi chế độ xem GL của bạn đã được hiển thị, nhưng trước khi bạn gọi presentRenderbuffer
để bộ đệm hiển thị sẽ chứa khung của bạn. Nếu không, hình ảnh kết quả sẽ rất có thể bị trống (bạn có thể thấy hành vi khác nhau giữa thiết bị và trình mô phỏng trên vấn đề cụ thể này).
- (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);
}
Cuối cùng, để lấy ảnh chụp màn hình bạn sử dụng renderInContext trong thông thường fasion. Tất nhiên, cái đẹp là bạn không cần phải lấy trực tiếp chế độ xem OpenGL. Bạn có thể lấy một trong những superviews của quan điểm OpenGL và có được một ảnh chụp màn hình sáng tác bao gồm các quan điểm OpenGL cùng với bất cứ điều gì khác bên cạnh nó hoặc trên đầu trang của nó:
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();
Tôi đang thử cách tiếp cận của bạn nhưng glReadPixels dường như không sao chép bất kỳ dữ liệu nào vào bộ đệm của tôi (biến "dữ liệu" trong ví dụ của bạn). Bất kỳ suy nghĩ nào về lý do tại sao điều này có thể xảy ra? Thiết lập của tôi là UIView thường xuyên có lớp sao lưu là CAEAGLLayer. Sau đó tôi thêm một UIImageView với một hình ảnh như là một đứa trẻ của chế độ xem GL-backed. Sau đó, tôi cố gắng lấy nội dung renderBuffer và bộ đệm của tôi vẫn là tất cả 0 như khi tôi khởi tạo nó với calloc. Tôi cũng đã cố gắng thiết lập lớp như là bộ lưu trữ renderbuffer nhưng không có gì. Nếu tôi thực hiện renderInContext thông thường, tôi sẽ lấy lại nội dung dưới dạng hình ảnh. – SaldaVonSchwartz