Захват вывода Isgl3d как изображения - PullRequest
10 голосов
/ 27 февраля 2012

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

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

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

Ответы [ 4 ]

5 голосов
/ 29 февраля 2012

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

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

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

3 голосов
/ 01 марта 2012

Я успешно использую этот фрагмент кода в одном из моих приложений для создания снимков экрана 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.

3 голосов
/ 01 марта 2012

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

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

Если вы не хотите сохранять вФотоальбом просто получит полученный 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

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

2 голосов
/ 15 декабря 2012

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

Шаги:

1

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

In 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]; в - (недействительно) 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's 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.

...