프로그래밍/OpenGL-ES2011. 1. 22. 13:00

이전글에서 사용한 프로젝트에서 텍스쳐를 붙혀볼랍니다.

프로젝트 첨부하였습니다.(N드라이브에서 파일을 올리니 올려지는구나 ~)







1.우선 사용할 텍스쳐를 추가합니다.

문제는 사용해야할수있는 텍스쳐의 사이즈가 정해져있습니다.

그거슨 2의 제곱....

Icon.png는 72x72의 사이즈 이고

Lena256.png는 파일명대로 256x256 입니다.

텍스쳐 싸이즈가 2의 제곱이 아닐경우 2의 제곱으로 만들어 줘야하나 생략합니다;

(애플 공식샘플에 존재하나 구글에서 찾는게 더 빠르니 nextPOT 으로 검색 )  POT는 -Power Of Two


그리고 텍스쳐를 읽어들이기 위해 CoreGraphics.framework를 추가해야합니다.

혹 CoreGraphics.framework 추가를 까먹고 빌드를 하면

_CG~궁시렁~  이런 에러가 나오는데 

프렘웤이나 라이브러리 추가안햇을경우의 에러출력 방식을 알수있기에 그냥 빌드해보시길 바랍니다;








2.텍스쳐읽어들이는 소스가 좀 길어서 새로 메쏘드를 작성합니다.

그리고 멤버변수인 텍스쳐 오브젝트도 같이 만들어 줍니다.





3.텍스쳐를 읽어들이기 전에 해야할일은

사용한 텍스쳐 삭제하기 입니다.

나중에 하면 까먹는 일이 많으니 제일먼저 해줍니다;






4.텍스쳐 로딩하기

간단하게 텍스쳐를 사용하기위한 멤버변수와 텍스쳐 파일명을 건네줍니다.(66,71라인)

넘겨준 멤버변수에 저장이 되기에 포인터로 넘겨줍니다.

로딩에 실패했을경우 NO가 반납되어

assert()에서 프로그램강제 종료되게 하였습니다.(69,74라인)






5.텍스쳐 로딩 메쏘드

UIImage로 텍스쳐를 가져오고있으나(482라인)

간단하게 하기위해 일단 캐쉬가 남는 imageNamed을 사용.

게임에서 쓸려면 imageWithData를 사용해야합니다.


픽셀 데이터를 저장하기 위해 malloc으로 메모리생성 (489라인)

메모리생성 했으니 반납하는것도 안까먹기 (497,564라인)

아이폰 아이패드용 텍스쳐 로딩처리(491 ~ 521 라인)


OpenGL에서의 텍스쳐 생성과 옵션설정(525 ~ 552 라인)

간혹 필터링 설정을 안하는 샘플이 있는데(529,531 라인)

에뮬레이터에서는 잘되는데 디바이스에서는 안되는 경우가 많습니다.

필터링도 종류가 많으니 옵션을 바꿔가면서 직접 눈으로 확인하는게 좋습니다.

( GL_LINEAR는 고퀄리티 속도저하/ GL_NEAREST는 저퀄리티 속도상승 )


어드레싱에 대해선 다음번에 쓸 atlas에서 쓰기로 하고(536라인)


환경설정은 GL_MODULATE가 디폴트 설정에 사각형 색상 무시 텍스쳐 출력이고(543라인)

GL_DECAL을 사용하면 사각형의 색깔과 텍스쳐의 그림이 같이 출력됩니다.


알파채널을 사용하는 GL_RGBA를 사용하니 블렌딩을 유효화(547라인)


마지막으로 GPU에게 전송하면 텍스쳐를 사용할 준비가 끝납니다.(552라인)





6.텍스쳐 좌표 설정

레나는 왼쪽위에구석을 기준으로 256싸이즈로 출력(197라인)

아이콘은 왼쪽밑에구석을 기준으로 72싸이즈로 출력되게 수정.


저번에 귀찮아서 생략한 색상도 추가하고(230라인)

4개의 정점의 색깔을 흰색으로 수정.


이번에 텍스쳐출력을 위해 텍스쳐좌표를 새롭게 작성.(243라인)

텍스쳐싸이즈가 1024픽쎌이던 32픽쎌이던 1.0이 최대싸이즈 입니다.

이건 다음에 쓸 atlas에서 다시 설명하겠습니다.

텍스쳐도 알기쉽게 퀘변조로의 Z 의 순서로 작성합니다.

소스 코멘트에서도 적었듯이 좌표로 치자면 x,y를(244라인)

다이렉트X에서는 u,v로 쓰고

오픈쥐엘에서는 s,t로 쓰나 둘다 같은놈입니다.


좌표랑 색상쓰려고 했던것처럼 텍스쳐도 똑같이 해줍니다(282라인)


glEnable(GL_TEXTURE_2D)을 써서 텍스쳐를 사용할것이라는걸 알리고(286라인)

텍스쳐생성할때 사용하였던 glBindTexture()를써서 사용할 텍스쳐오브젝트를 선택합니다(289라인)

첨에 mTexture를 바인드해서 사각형을 출력하였고(레나)

그담음엔 mTexture2를 바인드해서 사각형을 출력하였습니다.(아이콘)


기본적으로 렌더링이 끝나면 이 메쏘드에서 렌더링할때 유효화했던 스테이트를 원래상태로 돌려줍니다.(300~305라인)





이걸루 간단하게 텍스쳐를 출력해보았습니다.


위에처럼 텍스쳐를 쭈욱사용할경우 glEnable(GL_TEXTURE_2D)를 

텍스쳐 로딩할때 한번만 써줘서 286라인 생략가능합니다.(블렌드처럼)

그리고 glEnableClientState도 텍스쳐 로딩끝나고 한번만 설정해주면

매번 다시 설정할 필요는 없습니다.


단, 다른곳에서 glDisable을 안할경우에 해당됩니다. ㅎ_ㅎ;


다음엔 간단 텍스쳐 atlas를 해볼랍니다;

Posted by windship
프로그래밍/OpenGL-ES2011. 1. 22. 12:59

아이폰4랑 아이폰3는 해상도는 틀리나

다행히도 화면비율은 같습니다.

아이폰4 -> 640x960 비율 1:1.5 (세로모드일 경우)

아이폰3 -> 320x480 비율 1:1.5 (세로모드일 경우)




1.우선 정사각형으로 만들어 봅시다.

199라인에 있는 glOrthof()를 추가합니다.

gl.h를 보면 ( left, right, bottom, top, zNear, zFar ) 이렇게 6개의 파라메터를 넣으라 합니다.

저도 자세한건 모르니

현재 출력화면(다음부턴 viewport라 하겟슴)을 glOrthof()에 지정한 비율로 출력한다 라고 합시다.

결국 제대로된 정사각형을 출력하기 위해선 비율만 맞춰주면 됩니다.

여지껏 세로가 긴화면을 억지로 1:1의 비율로 출력됬으니 

이걸 1:1.5의 비율로 바꿔주기만 하면 됩니다.

3D공간은 원점이 가운데이고 상하좌우 ±1.0를 사용하여 전체싸이즈가 2.0이나

viewport는 +만써서 1.0의 싸이즈를 사용하니 

상하좌우 각각 ±1:1.5씩을 대입하여 전체싸이즈를 2배로 뿔려줍니다.


161라인의 setFramebuffer는 밑에서 설명.





2.EAGLView에서 viewport가 자동으로 설정됩니다.

디버그창에보이듯이 320x480으로 viewport를 설정되였습니다.

viewport는 여기서 설정됩니다를 위한것이기에 여기선 아무것도 안하고 패스합니다.






3.지대루 정사각형이 출력되어졌습니다.





4.위의 정사각형의 싸이즈는 둘다 1.0입니다.

좌표는 컬러풀이 (-0.5,-0.5)이고 검둠이고 (0,0)입니다.

이런식의 싸이즈와 좌표로는 원하는곳에 원하는 싸이즈의 사각형을 표시할 염두가 안납니다.

그래서 이걸 2D좌표와 2D싸이즈로 변활할 필요가 있습니다.

검둥이를 x160,y240 에 가로 80, 세로 120

컬러풀을 x0,y0 에 가로 240, 세로 360 으로 설정하면 이렇게 됩니다.







5.우선 사각형의 좌표와 싸이즈를 바꿔줍시다.

바꿔주는김에 사각형 그리는 순서도 알기쉽게 바꿔줍니다.

퀘변조로의 Z의 순서로.

179라인의 static const 삭제도 까먹으면 안됩니다.






6.이제 3D좌표를 2D좌표로 바꿔줍니다.

좌표X는 0.0 ~ 320.0  ------>  -1.0 ~ +1.0 으로 변환

좌표Y도 0.0 ~ 480.0  ------>  -1.5 ~ +1.5 으로 변환


-우선 2D좌표를 3D좌표로 바꿔줍니다.(좌표X의 경우)

2D좌표 / 320.0

2D좌표가 0.0 이면 0.0이 되고,

2D좌표가 320.0 이면 1.0이 됩니다.

(2D좌표는 +0.0 ~ +1.0이 되었습니다)


-이걸 2배로 늘려줍니다(2배라고는 하나 사실은 아까 설정한 좌우비율의 2배입니다)

0.0이면 0.0이 되고,

1.0이면 2.0이 됩니다.

(2D좌표는 +0.0 ~ +2.0이 되었습니다)


3D좌표의 -쪽으로 좌우비율의 반만큼 이동시켜줍니다.

0.0 이면 2D좌표 - 1.0 = -1.0이 되고,

2.0 이면 2D좌표 - 1.0 = +1.0이 됩니다.

(2D좌표는 -1.0 ~ +1.0이 되었습니다)


이걸 공식으로 하면

3DPosX = ( 2DPosX / 320.0f ) * ratioWidth - ( ratioWidth * 0.5f );



좌표Y도 이런식으로 바꿔주고 싶은데

OpenGL에서의 viewport좌표의 원점은 화면 왼쪽 밑에 존재하고

아이폰에서의 view좌표의 원점은 화면 왼쪽 위에에 존재하니

2배로 늘려줄때 곱하기 - 해줘서 부호를 반대로 만들어주고

위쪽이 + 가 되므로 상하비율의 반만큼 +쪽으로 이동시켜줍니다.


이걸 공식으로 하면

3DPosY = ( 2DPosY / 480.0f ) * -ratioHeight + ( ratioHeight * 0.5f );







이걸루 OpenGL에서 2D 사용의 기초가 끝났습니다.

샘플을 최대한 수정안하는 방향으로 했기에 실질적으로 사용할려면 이곳저곳 띁어 고쳐야 합니다.

잘못된 곳이 있으면 지적 부탁드립니다 ㅎ_ㅎ;



다음엔 텍스쳐를 붙혀볼랍니다.

Posted by windship
프로그래밍/OpenGL-ES2011. 1. 22. 12:58

아이폰에서 OpenGL|ES 1.0을 사용하여 2D를 사용하기위해

먼저 3D의 기본지식을 설명하려 합니다.

http://en.wikipedia.org/wiki/OpenGL_ES <-외국어로 씌어져 있슴.



사용환경

xcode 3.2.5

iOS sdk 4.2


1.우선 OpenGL프로젝트 생성.

저는 GL2D 란 이름으로 생성함.



2.OpenGL|ES 1.0을 쓰기위해선 자동생성된 ViewController 43번째줄만 바꿔주면 됩니다.

45~48번줄은 삭제하고 kEAGLRenderingAPIOpenGLES2를 kEAGLRenderingAPIOpenGLES1로 바꿔줌.

//opengl|es 1.0 사용

EAGLContext *aContext = [[EAGLContext allocinitWithAPI:kEAGLRenderingAPIOpenGLES1];






3.이번엔 실질적으로 렌더링하는곳을 몇군데 만져줍니다.

알기쉽게 사각형 순서를 코멘트하고,(163라인)

샘플의 사각형의 세로가 0.333 인게 기분나뻐서 0.5로 변경,(170~174라인)

3D좌표 설명용으로 검정색 정사각형을 하나로 추가하고(175~178라인)

배경색이 어두운게 기운이 안나서 서울지하철2호선색상으로 변경,(191라인)

2.0용 쉐이더 사용하는곳을 지워주고,(193라인근방)

위아래로 움직이는게 멀미할것 같으니 삭제,(200라인근방)

마지막으로 검정색 정사각형을 렌더링합니다.(207라인)-코멘트에 5번부터라고 써있는데 오타임;





4.빌드하고 실행하면 나오는 결과물입니다.

배경은 서울지하철 2호선 색상이고,

원래 샘플의 사각형보단 약간 길쭉하게 표시되고 정가운데에서 멈춰있으며,

제가 추가한 검정색 정사각형이 보입니다.

거기에 제가 코멘트에 적은 사각형 순서번호를 그렸습니다.




5.제가 추가한 사각형은 가로세로 1.0의 사이즈의 정사각형인데 직사각형으로 나옵니다.

원래 샘플의 사각형도 -0.5~0.5의 사이즈면 결국 1.0의 사이즈인데 마찬가지로 직사각형으로 나옵니다.

허나 이건 틀린게 아닙니다.





세로가 길던 가로가 길던 기본적인 3D좌표는 

가운데가 원점 

위아래(+1.0 ~ -1.0)

좌우(-1.0 ~ +1.0) 입니다.

( Z축은 왼손좌표일경우 앞뒤 +1.0 ~ -1.0 오른손좌표일경우 앞뒤 -1.0 ~ +1.0 )

제가 추가한 사각형은 사이즈가 1.0이니 위로 길죽하지만 

원점을 기준으로 정확하게 1.0의 사이즈로 출력되었고,

원래 샘플의 정사각형도 원점을 기준으로 위아래좌우로 0.5씩의 사이즈로 출력되었습니다.

그러므로 결론은 여기까진 아무문제 없다 입니다.





다음번엔 소스코드에 한줄만 추가해서 정사각형으로 만들고,

알기쉬운 2D좌표를(왼쪽위가 원점)써서 출력을 해볼까 합니다;

Posted by windship
프로그래밍/OpenGL-ES2010. 8. 3. 00:42
/**
 *  @file SurfaceView.h
 *  A View that implements a CoreSurface buffer
 *  The buffer is created automatically in initWithFrame: with the size
 *  passed to it and 5-6-5 RGB format.
 *  The memory can be accessed with the pixels property, and after modifying
 *  it you must call setNeedsDisplay on it for it to update.
 */
 
#import <UIKit/UIKit.h>
#import <CoreSurface/CoreSurface.h>
 
@interface SurfaceView : UIView
{
    CALayer *surfaceLayer;
    unsigned short *pixels;
    CoreSurfaceBufferRef surfaceBuffer;
}
 
@property (nonatomic, readonly) unsigned short *pixels;
 
@end
 
 
 
/**
 *  @file SurfaceView.m
 *  Implementation of SurfaceView
 */
 
#import "SurfaceView.h"
 
@implementation SurfaceView
 
- (id)initWithFrame:(CGRect)frame {
    frame.origin.x /= 2;
    frame.origin.y /= 2;
   
    if (self = [super initWithFrame:frame]) {
        // Create Surface
        int w = frame.size.width, h = frame.size.height;
        surfaceBuffer = CoreSurfaceBufferCreate((CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:w],         kCoreSurfaceBufferWidth,
            [NSNumber numberWithInt:h],         kCoreSurfaceBufferHeight,
            [NSNumber numberWithInt:'L565'],    kCoreSurfaceBufferPixelFormat,
            [NSNumber numberWithInt:2*w*h],     kCoreSurfaceBufferAllocSize,
            [NSNumber numberWithBool:YES],      kCoreSurfaceBufferGlobal,
            [NSNumber numberWithInt:2*w],       kCoreSurfaceBufferPitch,
            @"PurpleGFXMem",                    kCoreSurfaceBufferMemoryRegion,
            nil]);
       
        // Create layer for surface
        CGRect frame2 = CGRectMake(frame.origin.x, frame.origin.y,
                                   frame.size.width+1, frame.size.height+1);
        CoreSurfaceBufferLock(surfaceBuffer, 3);
        surfaceLayer = [[CALayer layer] retain];
        [surfaceLayer setMagnificationFilter:0];
        [surfaceLayer setEdgeAntialiasingMask:0];
        [surfaceLayer setFrame:frame2];
        [surfaceLayer setContents:surfaceBuffer];
        [surfaceLayer setOpaque:YES];
        [[self _layer] addSublayer:surfaceLayer];
        CoreSurfaceBufferUnlock(surfaceBuffer);
       
        // Get base address
        pixels = CoreSurfaceBufferGetBaseAddress(surfaceBuffer);
    }
    return self;
}
 
- (void)dealloc {
    [surfaceLayer release];
    [super dealloc];
}
 
- (void)drawRect:(CGRect)rect {
    // It will break if you remove this empty function
}
 
- (unsigned short*)pixels {
    return pixels;
}
@end

/** 
 *  @file SurfaceView.h
 *  A View that implements a CoreSurface buffer
 *  The buffer is created automatically in initWithFrame: with the size
 *  passed to it and 5-6-5 RGB format.
 *  The memory can be accessed with the pixels property, and after modifying
 *  it you must call setNeedsDisplay on it for it to update.
 */
 
#import <UIKit/UIKit.h>
#import <CoreSurface/CoreSurface.h>

@interface SurfaceView : UIView 
{
    CALayer *surfaceLayer;
    unsigned short *pixels;
    CoreSurfaceBufferRef surfaceBuffer;
}

@property (nonatomic, readonly) unsigned short *pixels;

@end



/** 
 *  @file SurfaceView.m
 *  Implementation of SurfaceView
 */
 
#import "SurfaceView.h"

@implementation SurfaceView

- (id)initWithFrame:(CGRect)frame {
    frame.origin.x /= 2;
    frame.origin.y /= 2;
    
    if (self = [super initWithFrame:frame]) {
        // Create Surface
        int w = frame.size.width, h = frame.size.height;
        surfaceBuffer = CoreSurfaceBufferCreate((CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:w],         kCoreSurfaceBufferWidth,
            [NSNumber numberWithInt:h],         kCoreSurfaceBufferHeight,
            [NSNumber numberWithInt:'L565'],    kCoreSurfaceBufferPixelFormat,
            [NSNumber numberWithInt:2*w*h],     kCoreSurfaceBufferAllocSize,
            [NSNumber numberWithBool:YES],      kCoreSurfaceBufferGlobal,
            [NSNumber numberWithInt:2*w],       kCoreSurfaceBufferPitch,
            @"PurpleGFXMem",                    kCoreSurfaceBufferMemoryRegion,
            nil]);
        
        // Create layer for surface
        CGRect frame2 = CGRectMake(frame.origin.x, frame.origin.y, 
                                   frame.size.width+1, frame.size.height+1);
        CoreSurfaceBufferLock(surfaceBuffer, 3);
        surfaceLayer = [[CALayer layer] retain];
        [surfaceLayer setMagnificationFilter:0];
        [surfaceLayer setEdgeAntialiasingMask:0];
        [surfaceLayer setFrame:frame2];
        [surfaceLayer setContents:surfaceBuffer];
        [surfaceLayer setOpaque:YES];
        [[self _layer] addSublayer:surfaceLayer];
        CoreSurfaceBufferUnlock(surfaceBuffer);
        
        // Get base address
        pixels = CoreSurfaceBufferGetBaseAddress(surfaceBuffer);
    }
    return self;
}

- (void)dealloc {
    [surfaceLayer release];
    [super dealloc];
}

- (void)drawRect:(CGRect)rect {
    // It will break if you remove this empty function
}

- (unsigned short*)pixels {
    return pixels;
}
@end
Posted by windship
프로그래밍/OpenGL-ES2010. 7. 20. 10:45

휴...거진 처음 시작한지 3주만에 알게 되네요... 개념도 없었다가...

 

대부분 게임 어플을 보면, 오픈지엘 100% 를 사용하고, UI 작업도 오픈지엘 에서 하고 있네요...

 

아이폰 xib UI 를 해보닌깐, 화면하나 꾸미는데 큰 어려움이 없던데...

(아직 table 주무리기 하나만 남은것 같지만...ㅋ)

 

이전 작업할때에도 마찬가지였지만, UI 작업을 코딩으로 하게되면, 창 하나 꾸미는데,

하루, 진짜 아주 열심히 하면, 창 2-3개정도 만들수 있는데, 그렇다고 UI 작업이라 아마도 인정안해 주더라구요..ㅋ

 

아이폰에서는 본 게임 부분은 Opelgl (당연히 속도 때문에...)

 

UI 관련된 부분등은 xib 띄워서, 버튼등으로 생성하고 하는게 좋더라구요.경고창이나 액션창도 그대로 사용하면 될테고...

 

창하나 꾸미는데 2-3시간 걸리던 것이, 2~30분정도로 단축되네요..^^

 

단 주의해야할 것이 있던데...

 

전 처음에 프로젝트 생성하면, opengl 쪽도 xib 로 생성해서, addsubview 로 자유자재로 이동하려고 생각했었다가,

나중에 낭패를 보았습니다 (물론 제가 실수한건지 모르겠지만...)

 

처음 프로젝트를 만들때, window 기반에 window 표면을 opengl class 로 설정하고,

그 위에 xib UI 창을 addsubview 로 하면 간편히 띄워 지더라구요..여기에, 로그인화면, 대기실 등등...꾸미면

되겠던데요...

 

그러닌깐 개념이 오픈지엘 베이스에 그 창 위에다가 xib 창을 덮어쓰고, 사용이 끝나면 그 창을 removeromsuperview

해 버리면 간단히 해결되었습니다.(다른창은 addsubview 로 다시 띄우면 되겠지요~)

 

물론 그때에도 내부 opengl 타임이 가동될것이라 예상이 되구요(아직 해보진 않았지만..ㅋㅋ)

그럴땐 상태 변수로 채크해서 잠시 멈춰서 사용하면 될테구요~~~

 

아직도 전체적인 개념은 못잡고 있지만, 그나저나, 아이폰 오픈지엘 관련서적은 어디서 구해야하는지...ㅋ

Posted by windship