Захват вывода Isgl3d как изображение

У меня есть куча трудностей с моментальным снимком UIImage контрольного представления Isgl3d. Кажется, что бы я ни делал, я просто заканчиваю черным квадратом.

У меня есть рабочий вид камеры и 3d-модель, на мой взгляд, я стараюсь использовать как буферные методы, так и регулярные снимки экрана, чтобы получить изображение из него, но без какого-либо плодотворного результата.

Есть ли у кого-нибудь исходный код, где они успешно снимают представление Isgl3d?

Ответ 1

Вот инструкции Apple и официальный код для моментального снимка GL-представления на UIImage (учитывает отображение сетчатки, перевернутые координаты и т.д.), и я успешно ее использовал. Конечно, это не специфичный для iSGL3D, но до тех пор, пока вы можете получить подходящий контекст и фреймбуфер для привязки, он должен поступать правильно. (Как отмечает страница, вы должны обязательно сделать снимок перед вызовом -presentRenderbuffer:, чтобы действительный буфер был действителен.)

https://developer.apple.com/library/ios/#qa/qa1704/_index.html

У меня есть только беглое знакомство с iSGL3D-библиотекой, и не похоже, что там есть очевидные крючки, чтобы вы могли визуализировать сцену, но не представлять ее (или сначала визуализировать ее внеэкранный буфер). Место, где может потребоваться вмешательство, находится в методе -finalizeRender подкласса Isgl3dGLContext, который вы используете, непосредственно перед вызовом -presentRenderbuffer. Этот контекст является внутренним классом каркаса здесь, поэтому вам может потребоваться немного перетасовать вещи в библиотеке, чтобы настроить (сказать) делегата из контекста, который работает из представления, и директора, чтобы в конечном итоге спросить ваше приложение предпримите любые действия непосредственно перед "настоящим" вызовом, в течение которых вы можете выбрать запуск кода скриншота, если хотите, или ничего не делать, если не хотите.

Ответ 2

Это то, что вы хотите?

Это займет скриншот из текущего контекста и фреймбуфера и сохранит его в фотоальбоме.

Если вы не хотите сохранять в фотоальбом, просто получите полученный UIImage в конце.

Также не забудьте вызвать это только после завершения рисования, но перед переключением буферов.

Также, если вы используете MSAA, это нужно вызывать после glResolveMultisampleFramebufferAPPLE и привязки нового буфера.

#ifdef AUTOSCREENSHOT

// callback for CGDataProviderCreateWithData
void releaseData(void *info, const void *data, size_t dataSize) {
    free((void*)data);
}

// callback for UIImageWriteToSavedPhotosAlbum
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"Save finished");
    [image release];
}

-(void)saveCurrentScreenToPhotoAlbum {
    int height = (int)screenSize.y*retina;
    int width = (int)screenSize.x*retina;

    NSInteger myDataLength = width * height * 4;
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    for(int y = 0; y <height; y++) {
        for(int x = 0; x < width * 4; x++) {
            buffer2[(int)((height - 1 - y) * width * 4 + x)] = buffer[(int)(y * 4 * width + x)];
        }
    }
    free(buffer);   

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, releaseData);
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * width;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    CGColorSpaceRelease(colorSpaceRef);
    CGDataProviderRelease(provider);

    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
    CGImageRelease(imageRef);

    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

#endif

Я использую этот код для сохранения временных скриншотов, пока я играю, поэтому у меня есть материал для бонуса, чтобы положить его в магазин приложений.

Ответ 3

Я использую этот фрагмент кода успешно в одном из моих приложений, чтобы делать скриншоты OpenGL.

enum {
  red,
  green,
  blue,
  alpha
};

- (UIImage *)glToUIImage {
  CGSize glSize = self.glView.bounds.size;
  NSInteger bufDataLen = glSize.width * glSize.height * 4;

  // Allocate array and read pixels into it.
  GLubyte *buffer = (GLubyte *)malloc(bufDataLen);
  glReadPixels(0, 0, glSize.width, glSize.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

  // We need to flip the image
  NSUInteger maxRow = (NSInteger)glSize.height - 1;
  NSUInteger bytesPerRow = (NSInteger)glSize.width * 4;

  GLubyte *buffer2 = (GLubyte *)malloc(bufDataLen);
  for(int y = maxRow; y >= 0; y--) {
    for(int x = 0; x < bytesPerRow; x+=4) {
      NSUInteger c0 = y * bytesPerRow + x;
      NSUInteger c1 = (maxRow - y) * bytesPerRow + x;
      buffer2[c0+red] = buffer[c1+red];
      buffer2[c0+green] = buffer[c1+green];
      buffer2[c0+blue] = buffer[c1+blue];
      buffer2[c0+alpha] = buffer[c1+alpha];
    }
  }
  free(buffer);

  // Make data provider with data
  CFDataRef imageData = CFDataCreate(NULL, buffer2, bufDataLen);
  free(buffer2);

  CGDataProviderRef provider = CGDataProviderCreateWithCFData(imageData);
  CFRelease(imageData);

  // Bitmap format
  int bitsPerComponent = 8;
  int bitsPerPixel = 32;
  CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
  CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
  CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

  // Create the CGImage
  CGImageRef imageRef = CGImageCreate(glSize.width,
                                      glSize.height,
                                      bitsPerComponent,
                                      bitsPerPixel,
                                      bytesPerRow,
                                      colorSpaceRef,
                                      bitmapInfo,
                                      provider,
                                      NULL,
                                      NO,
                                      renderingIntent);

  // Clean up
  CGColorSpaceRelease(colorSpaceRef);
  CGDataProviderRelease(provider);

  // Convert to UIImage
  UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
  CGImageRelease(imageRef);

  return [image autorelease];
}

Убедитесь, что вы связали буфер кадра, прежде чем делать это, например

glBindFramebufferOES(GL_FRAMEBUFFER_OES, myFrameBuffer);
glViewport(0, 0, myBackingWidth, myBackingHeight);

И вызовите -glToUIImage до того, как будет представлен буфер кадра!

Для получения дополнительной информации Apple предоставляет пример кода для снятия скриншотов из OpenGL.

Ответ 4

Я придумал это возможное решение. Вам нужно немного изменить библиотеку isgl3d.

Шаги:

1.

Создать делегат для Isgl3dGLContext1:

В Isgl3dGLContext1.h

@protocol ScreenShooterDelegate;

#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import "Isgl3dGLContext.h"

@interface Isgl3dGLContext1 : Isgl3dGLContext {

    NSObject<ScreenShooterDelegate>* __unsafe_unretained delegate;

    GLuint _colorRenderBuffer;
@private
    EAGLContext * _context;

    // The OpenGL names for the framebuffer and renderbuffer used to render to this view
    GLuint _defaultFrameBuffer;


    GLuint _depthAndStencilRenderBuffer;
    GLuint _depthRenderBuffer;
    GLuint _stencilRenderBuffer;

    // OpenGL MSAA buffers
    GLuint _msaaFrameBuffer;
    GLuint _msaaColorRenderBuffer;

    GLuint _msaaDepthAndStencilRenderBuffer;
    GLuint _msaaDepthRenderBuffer;
    GLuint _msaaStencilRenderBuffer;
}

- (id) initWithLayer:(CAEAGLLayer *) layer;
@property (assign) NSObject<ScreenShooterDelegate>* delegate;
@property  BOOL takePicture;
@property  GLuint colorRenderBuffer;

@end

@protocol ScreenShooterDelegate


@optional

- (void)takePicture;

@end

2.

. Добавьте этот код в Isgl3dGLContext1.m:

@synthesize takePicture;
@synthesize colorRenderBuffer = _colorRenderBuffer;

Перед строкой [_context presentRenderbuffer: GL_RENDERBUFFER_OES]; in - (void) finalizeRender:

if(takePicture){
        takePicture=NO;
        if([delegate respondsToSelector:@selector(takePicture)]){
            [delegate takePicture];
        }
    }

3 Поместите этот код в класс, где вы хотите сделать снимок экрана:

In Class.h add <ScreenShooterDelegate>

В методе Class.m

[Isgl3dDirector sharedInstance].antiAliasingEnabled = NO;

Photos3DAppDelegate *appDelegate = (Photos3DAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.inOutSceneView showSphere];

Isgl3dEAGLView* eaglview=(Isgl3dEAGLView*)[[Isgl3dDirector sharedInstance] openGLView];
Isgl3dGLContext1 * _glContext=(Isgl3dGLContext1*)[eaglview glContext];
_glContext.delegate=self;
_glContext.takePicture=YES;

В методе - (void) takePicture {} Поместите код из Apple и в конце метода добавьте [Isgl3dDirector sharedInstance].antiAliasingEnabled = YES; (Если вы его используете)

//https://developer.apple.com/library/ios/#qa/qa1704/_index.html

-(void)takePicture{


NSLog(@"Creating Foto");

GLint backingWidth, backingHeight;

Isgl3dEAGLView* eaglview=(Isgl3dEAGLView*)[[Isgl3dDirector sharedInstance] openGLView];
//Isgl3dGLContext1 * _glContext=(Isgl3dGLContext1*)[eaglview glContext];
//glBindRenderbufferOES(GL_RENDERBUFFER_OES, _glContext.colorRenderBuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &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);

// OpenGL ES measures data in PIXELS
// Create a graphics context with the target size measured in POINTS
NSInteger widthInPoints, heightInPoints;
if (NULL != UIGraphicsBeginImageContextWithOptions) {
    // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
    // Set the scale parameter to your OpenGL ES view contentScaleFactor
    // so that you get a high-resolution snapshot when its value is greater than 1.0
    CGFloat scale = eaglview.contentScaleFactor;
    widthInPoints = width / scale;
    heightInPoints = height / scale;
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale);
}
else {
    // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
    widthInPoints = width;
    heightInPoints = height;
    UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints));
}

CGContextRef cgcontext = UIGraphicsGetCurrentContext();

// 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(cgcontext, kCGBlendModeCopy);
CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);

// Retrieve the UIImage from the current context
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

// Clean up
free(data);
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

[Isgl3dDirector sharedInstance].antiAliasingEnabled = YES;
}

Примечание. Для меня работала только комментирование glBindRenderbufferOES (GL_RENDERBUFFER_OES, _colorRenderbuffer); а также В вашем случае вы можете сделать эти шаги с Isgl3dGLContext2 вместо Isgl3dGLContext1.