Friday, May 15, 2009

OpenGL ES for iPhone : Part 4 with More Drawings and OpenGL Screen Save

In some OpenGL 1.x books you might notice that the gl commands (like belows) are within the code block of glBegin() and glEnd() pair. These gl* commands must be converted to Vertices Array in order to be useful for OpenGL ES for iPhone.


glBegin(GL_LINE_STRIP);
z = -50.0f;
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f) { x = 50.0f*sin(angle); y = 50.0f*cos(angle); // Specify the point and move the z value up a little glVertex3f(x, y, z); z += 0.5f; } // Done drawing points glEnd();


Typically, you remove the glBegin() and glEnd() commands (that is immediate mode) and create the vertices array and implement the vertices position calculation (if any) inside the setupView and then remove other unsupported gl* commands before putting them to the iPhone OpenGL ES project code.

This is an example converting from the immediate mode to vertex arrays

immediate mode

glBegin(GL_TRIANGLES);
glVertex3f(-2.0, 0.5, 0.0);
glVertex3f(0.0, 4.0, -2.0);
glVertex3f(1.5, 2.5, -0.5);
glEnd();


vertex arrays

GLfloat vertices[] = { -2.0, 0.5, 0.0, 0.0, 4.0, -2.0, 1.5, 2.5, -0.5 };
glVertexPointer(3, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);


OpenGL ES does not support the full set of vertex array functions or parameters present in OpenGL






























FunctionNotes
glBegin()Not supported.
glEnd()Not supported.
glEdgeFlag[v]()Not supported.
glVertex{234}{sifd}[v]()Not supported.
glNormal3f()Supported.
glNormal3{bsifd}[v]()Not supported.
glNormal3{bsifd}[v]()Not supported.
glTexCoord{1234}{sifd}[v]()Not supported.
glMultiTexCoord4f()Supported.
glMultiTexCoord{1234}{sifd}[v]()Not supported.
glColor4f()Supported.
glColor{34}{bsifd ub us ui}[v]()Not supported.
glIndex{sifd ub}[v]()Not supported.
glVertexPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but support for
GL_BYTE has been added.
glNormalPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but support for
GL_BYTE has been added.
glColorPointer()Supported.
Type cannot be GL_INT or GL_DOUBLE, but
support for GL_UNSIGNED_BYTE has been added.
In addition, the alpha value must be included with all colors;
there is no support for specifying only the RGB values.
glIndexPointer()Not supported.
glTexCoordPointer()Supported.
 Type cannot be GL_INT or GL_DOUBLE, but support
for GL_BYTE has been added. Also, because there is no support
for 1D textures, at least 2 texture coordinates must be provided
per vertex.
glEdgeFlagPointer()Not supported.
glInterleavedArrays()Not supported.
glArrayElement()Not supported.
glDrawArrays()GL_POINTS, GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES,
GL_TRIANGLE_STRIP, and GL_TRIANGLE_FAN are supported.
GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON are not supported.
glDrawElements()GL_POINTS, GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES,
GL_TRIANGLE_STRIP, and GL_TRIANGLE_FAN are supported.
GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON are not
supported. Type must either be GL_UNSIGNED_BYTE or
GL_UNSIGNED_SHORT (not GL_UNSIGNED_INT).
glDrawRangeElements()Supported.
glEnableClientState()Valid for all supported attributes.
glDisableClientState()Valid for all supported attributes.


Here are some of the typical drawings in 3D. This one is for a rotating Spiral


To use the source codes here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.m from below and paste them for Build & Go in XCode.

EAGLView.m (for Spiral) Select all

//
// EAGLView.m
// Spiral
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);

glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c = 0;
z = -50.0f;

// Loop around in a circle three times
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f) { // Calculate x and y values on the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bump up the z value z += 0.5f; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 0.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 0.0f, 0.0f, 0.0f); glRotatef(yRot, 1.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Set Line Width glLineWidth(3.0f); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; xRot += 0.1 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } - (void)layoutSubviews { [EAGLContext setCurrentContext:context]; [self destroyFramebuffer]; [self createFramebuffer]; [self drawView]; } - (BOOL)createFramebuffer { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); if (USE_DEPTH_BUFFER) { glGenRenderbuffersOES(1, &depthRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); } if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); return NO; } return YES; } - (void)destroyFramebuffer { glDeleteFramebuffersOES(1, &viewFramebuffer); viewFramebuffer = 0; glDeleteRenderbuffersOES(1, &viewRenderbuffer); viewRenderbuffer = 0; if(depthRenderbuffer) { glDeleteRenderbuffersOES(1, &depthRenderbuffer); depthRenderbuffer = 0; } } - (void)startAnimation { self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } - (void)stopAnimation { self.animationTimer = nil; } - (void)setAnimationTimer:(NSTimer *)newTimer { [animationTimer invalidate]; animationTimer = newTimer; } - (void)setAnimationInterval:(NSTimeInterval)interval { animationInterval = interval; if (animationTimer) { [self stopAnimation]; [self startAnimation]; } } - (void)dealloc { [self stopAnimation]; if ([EAGLContext currentContext] == context) { [EAGLContext setCurrentContext:nil]; } [context release]; [super dealloc]; } @end


And this one is for a rotating Fanned Circle.



EAGLView.m (Fanned Circle) Select all

//
// EAGLView.m
// Fanned Circle
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);


glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c;
z = 0.0f;
c = 0;
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f)) { // Top half of the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bottom half of the circle x = 50.0f*sin(angle+GL_PI); y = 50.0f*cos(angle+GL_PI); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 1.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Setup and render the points glEnable(GL_POINT_SMOOTH); glPointSize(1.0); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; zRot+=1.2 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } - (void)layoutSubviews { [EAGLContext setCurrentContext:context]; [self destroyFramebuffer]; [self createFramebuffer]; [self drawView]; } - (BOOL)createFramebuffer { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); if (USE_DEPTH_BUFFER) { glGenRenderbuffersOES(1, &depthRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); } if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); return NO; } return YES; } - (void)destroyFramebuffer { glDeleteFramebuffersOES(1, &viewFramebuffer); viewFramebuffer = 0; glDeleteRenderbuffersOES(1, &viewRenderbuffer); viewRenderbuffer = 0; if(depthRenderbuffer) { glDeleteRenderbuffersOES(1, &depthRenderbuffer); depthRenderbuffer = 0; } } - (void)startAnimation { self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } - (void)stopAnimation { self.animationTimer = nil; } - (void)setAnimationTimer:(NSTimer *)newTimer { [animationTimer invalidate]; animationTimer = newTimer; } - (void)setAnimationInterval:(NSTimeInterval)interval { animationInterval = interval; if (animationTimer) { [self stopAnimation]; [self startAnimation]; } } - (void)dealloc { [self stopAnimation]; if ([EAGLContext currentContext] == context) { [EAGLContext setCurrentContext:nil]; } [context release]; [super dealloc]; } @end


And also I have found a very nice method saveCurrentScreenToPhotoAlbum to capture the OpenGL view screen here. And below is an implementation on how to capture the screen in iPhone Simulator. You just touch/click the info button at the lower left bottom of iPhone Screen to trigger the screen capture to the Photo Album (Simulator or actual device).

To use the source codes here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.m from below and paste them for Build & Go in XCode. For this screenshot functionality, you need to add the CoreGraphics Framework to the Xcode Project before build & go.




EAGLView.m (Fanned Circle with Screen Capture) Select all

//
// EAGLView.m
// Fanned Circle with Screen Capture
//
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}

#define kAnimationFrequency 60.0


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}

animationInterval = 1.0 / kAnimationFrequency;
[self setupView];
}
return self;
}


#define GL_PI 3.1415f
GLfloat linesVertices[186];

- (void)setupView {

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:self action:@selector(saveCurrentScreenToPhotoAlbum) forControlEvents:UIControlEventTouchUpInside];
infoButton.alpha = 0.5f;
infoButton.frame = CGRectMake(17, self.bounds.size.height-33, 33, 33);
[self addSubview:infoButton];
[self bringSubviewToFront:infoButton];

// setup the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GLfloat nRange = 100.0f;
GLfloat w = 320.0f, h = 480.0f;
glOrthof (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);


glMatrixMode(GL_MODELVIEW);

GLfloat x,y,z,angle; // Storage for coordinates and angles
int c;
z = 0.0f;
c = 0;
for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI / 20.0f)) { // Top half of the circle x = 50.0f*sin(angle); y = 50.0f*cos(angle); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; // Bottom half of the circle x = 50.0f*sin(angle+GL_PI); y = 50.0f*cos(angle+GL_PI); // glVertex3f(x, y, z); linesVertices[c++] = x; linesVertices[c++] = y; linesVertices[c++] = z; } } - (void)drawView { static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; static GLfloat zRot = 1.0f; const GLubyte linesColors[] = { 0.0f, 1.0f, 0.0f, 1.0f, }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // Clear background color glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); // Setup and render the points glEnable(GL_POINT_SMOOTH); glPointSize(1.0); glVertexPointer(3, GL_FLOAT, 0, linesVertices); // Set drawing color to green glColor4f(0.0f, 1.0f, 0.0f, 0.0f); glColorPointer(4, GL_UNSIGNED_BYTE, 0, linesColors); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES, 0, 189); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; static NSTimeInterval lastDrawTime; if (lastDrawTime) { NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime; zRot+=0.1 * timeSinceLastDraw; } lastDrawTime = [NSDate timeIntervalSinceReferenceDate]; } // callback for CGDataProviderCreateWithData void releaseScreenshotData(void *info, const void *data, size_t size) { free((void *)data); }; // callback for UIImageWriteToSavedPhotosAlbum - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { NSLog(@"ScreenSave finished\n"); [image release]; // release image } - (void)saveCurrentScreenToPhotoAlbum { NSInteger myDataLength = backingWidth * backingHeight * 4; // allocate array and read pixels into it. GLuint *buffer = (GLuint *) malloc(myDataLength); glReadPixels(0, 0, backingWidth, backingHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer); // gl renders “upside down” so swap top to bottom into new array. for(int y = 0; y < x =" 0;" top =" buffer[y" bottom =" buffer[(backingHeight" provider =" CGDataProviderCreateWithData(NULL," bitspercomponent =" 8;" bitsperpixel =" 4" bytesperrow =" 4" colorspaceref =" CGColorSpaceCreateDeviceRGB();" bitmapinfo =" kCGBitmapByteOrderDefault;" renderingintent =" kCGRenderingIntentDefault;" imageref =" CGImageCreate(320," myimage =" [[UIImage" viewframebuffer =" 0;" viewrenderbuffer =" 0;" depthrenderbuffer =" 0;" animationtimer =" [NSTimer" animationtimer =" nil;" animationtimer =" newTimer;" animationinterval =" interval;">


In case you might also want to know how to do screenshot for a non-OpenGL ES content programatically. This is the code for viewcontroller. If you put the code in a UIView object change self.view to self

- (void) snapUIView
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil);
}





1 comment:

Arseni Buinitsky said...

That iPhone posts are not so bad, thanks. I am learning OpenGL ES currently to use it in my app.

Could you find some time to write about this OES_framebuffer_object extension (or just point me to docs), i'm not getting it. Theory is clear, but I somewhy can't find how to actually use it (i.e. which functions are defined and what they actually do).

I need to render some simple geometric object to offscreen surface (and this I suppose should be renderbuffer) and draw that surface to screen (with other objects), preserving it's state each time for some analysis.