OpenGL ES 게임 프레임웍

아이폰 2011. 2. 14. 16:04



이 포스트는 작성중... -_-;; 음 마무리 지어야 하는데 이런 저런 일들로 정리를 못하고 있네;;;




메시 사각영역을 스크린 사각영역으로 변환 
-(CGRect)screenRectFromMeshRect:(CGRect)rect atPoint:(CGPoint)meshCenter
{
	// find the point on the screen that is the center of the rectangle
	// and use that to build a screen-space rectangle
	CGPoint screenCenter = CGPointZero;
	CGPoint rectOrigin = CGPointZero;
	// since our view is rotated, then our x and y are flipped
	screenCenter.x = meshCenter.y + 160.0; // need to shift it over
	screenCenter.y = meshCenter.x + 240.0; // need to shift it up
	
	rectOrigin.x = screenCenter.x - (CGRectGetHeight(rect)/2.0); // height and width 
	rectOrigin.y = screenCenter.y - (CGRectGetWidth(rect)/2.0); // are flipped
	
	return CGRectMake(rectOrigin.x, rectOrigin.y, CGRectGetHeight(rect), CGRectGetWidth(rect));
}



SceneObject 업데이트 메소드 
-(void)update
{
	glPushMatrix();
	glLoadIdentity();
	
	// move to my position
	glTranslatef(translation.x, translation.y, translation.z);
	
	// rotate
	glRotatef(rotation.x, 1.0f, 0.0f, 0.0f);
	glRotatef(rotation.y, 0.0f, 1.0f, 0.0f);
	glRotatef(rotation.z, 0.0f, 0.0f, 1.0f);
	
	//scale
	glScalef(scale.x, scale.y, scale.z);
	// save the matrix transform
	glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
	//restore the matrix
	glPopMatrix();
	if (collider != nil) [collider updateCollider:self];
}



SceneObject 렌더메소드 
-(void)render
{
	if (!mesh || !active) return; // if we do not have a mesh, no need to render
	// clear the matrix
	glPushMatrix();
	glLoadIdentity();
	glMultMatrixf(matrix);
	[mesh render];	
	glPopMatrix();
}


OpenGL ES 사용설정

아이폰 2011. 2. 13. 01:51


 OpenGL ES 를 사용하기 위한 설정방법을 알아보자. 
아이폰에서는 Core Animation의 CAEAGLayer 클래스를 통하여,  OpenGL ES 엔진을 사용한  그래픽 하드웨어 연산을
추상화 해준다.  UIView의 서브클래스에 일반 코어애니메이션 레이어가 아닌 CAEAGLayer 를 추가하면,   UIView의
 drawRect 메소드가 아닌 표준 OpenGL ES 함수호출을 통헤 드로잉 작업을 할 수 있게된다. 

OpenGL ES를 사용하기 위해 EAGLView 클래스를 생성해보자.  "Beginning iPhone Game Development (출판사:Apress) " 를
참고하였다. (물론 이 책도 애플 OpenGL ES 개발자 레퍼런스의 샘플을 수정한 예제를 제공한 것이다.)

1. 먼저 OpenGLES.framework와 QuartzCore.framework 를 프레젝트에 추가한다 


2. EAGLView 클래스 정의는 다음과 같다. 
#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

@interface EAGLView : UIView {
    
@private
    // 백버퍼의 넓이, 높이 
    GLint backingWidth;
    GLint backingHeight;
    
    EAGLContext *context;
   
 // 렌더버퍼, 프레임버퍼 식별자
    GLuint viewRenderbuffer, viewFramebuffer;
    
   // 깊이 버퍼, 사용하지 않으면 0
    GLuint depthRenderbuffer;
}

- (void)beginDraw;
- (void)finishDraw;
- (void)setupViewLandscape;
- (void)setupViewPortrait;

3.  UIView 클래스의  +(Class)layerClass 메소드를 오버라이드하여, CAEAGLayer 반환하도록 하여 OpenGL 드로잉을 가능하게 한다
+ (Class)layerClass {
    return [CAEAGLLayer class];
}

4.  드로잉 목적에 맞게 CAEAGLayer 의 속성을 설정하고, Context 생성 
- (id)initWithFrame:(CGRect)rect {
    
    if ((self = [super initWithFrame:rect])) {
        // 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;
        }
		self.multipleTouchEnabled = YES;
	}
    return self;
}


5. 프레임 버퍼와 렌더링 버퍼를 생성한다. 
렌더버퍼는 색상 등의 데이터로 채워진 2차원 표면이고, 프레임버퍼는 여러 렌더버퍼로 구성된다.
(이 예제에서 프레임버터에 렌더버퍼와 깊이 버퍼를 연결하였다. )
- (void)layoutSubviews 
{
	[EAGLContext setCurrentContext:context];
	[self destroyFramebuffer];
	[self createFramebuffer];
	[self setupViewLandscape];
}

- (BOOL)createFramebuffer {    
    // 프레임버퍼와, 렌더버퍼를 위한 식별자 값 얻기 (식별자는 GLuint 형 타입)
    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;
    }
}


6. 뷰포트와 투영변환을  설정한다 
- (void)setupViewLandscape
{		
	// Sets up matrices and transforms for OpenGL ES
	glViewport(0, 0, backingWidth, backingHeight);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
	// set up the viewport so that it is analagous to the screen pixels
	glOrthof(-backingHeight/2.0, backingHeight/2.0, -backingWidth/2.0, backingWidth/2.0, -1.0f, 1.0f);
	
	glMatrixMode(GL_MODELVIEW);
	
	// Clears the view with black
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

7.  카메라를 위치를 설정한다. 
-(void)perspectiveFovY:(GLfloat)fovY 
                        aspect:(GLfloat)aspect 
			 zNear:(GLfloat)zNear
			   zFar:(GLfloat)zFar 
{
	const GLfloat pi = 3.1415926;
	//	Half of the size of the x and y clipping planes.
	// - halfWidth = left, halfWidth = right
	// - halfHeight = bottom, halfHeight = top
	GLfloat halfWidth, halfHeight;
	//	Calculate the distance from 0 of the y clipping plane. Basically trig to calculate
	//	position of clip plane at zNear.
	halfHeight = tan( (fovY / 2) / 180 * pi ) * zNear;	
	//	Calculate the distance from 0 of the x clipping plane based on the aspect ratio.
	halfWidth = halfHeight * aspect;
	//	Finally call glFrustum with our calculated values.
	glFrustumf( -halfWidth, halfWidth, -halfHeight, halfHeight, zNear, zFar );
}


8. 렌더링한다. 
-(void)beginDraw
{
	// Make sure that you are drawing to the current context
	[EAGLContext setCurrentContext:context];
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
	// make sure we are in model matrix mode and clear the frame
	glMatrixMode(GL_MODELVIEW);
	glClear(GL_COLOR_BUFFER_BIT);
	// set a clean transform
	glLoadIdentity();	
}

-(void)finishDraw
{
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
	[context presentRenderbuffer:GL_RENDERBUFFER_OES];	
}


실제로 5번 과정까지가 EAGLView를 사용하기 위한 준비단계이며 한번 설정이 되면 거의 바뀌지 않는 부분들이다. 
6번 이후 과정부터는  애플리케이션에 따라 사용자 입력에 대한 렌더링을  수행하는 작업들이다.