폴리곤에 이미지를 맵핑하는 텍스춰맵핑을 다루려고 합니다. 텍스춰맵핑이 가능해야 디자이너가 그려준 멋진 그림을 화면에 그릴 수 있습니다. 텍스춰맵핑은 아래의 순서대로 이뤄집니다.
텍스춰로 사용할 이미지를 불러오고 이미지로 부터 데이터 바이트 배열을 얻어온다.
텍스춰를 생성하고 텍스춰이름(텍스춰구분자)를 얻는다.
텍스춰를 바인딩하고 텍스춰 좌표를 활성화한다.
폴리곤을 그린다.
그다지 복잡하지 않습니다. iOS에서 이미지 데이터의 바이트 배열을 얻어 오는 것은 코어그래픽스의 도움을 받아야 합니다. UIImage 에서 바로 해당 이미지의 데이터에 접근할 수 없기 때문입니다. UIImage로 불러온 이미지를 코어그래픽스를 통해 이미지 데이터 배열을 받아오는 순서는 아래와 같습니다.
UIImage로 이미지를 불러온다
UIImage 에서 CGImageRef 를 얻는다
이미지 데이터를 담을 버퍼와 함께 CGBitmapContext를 만든다
CGImage를 CGBitmapContext에 그린다.
이미지 데이터를 담을 버퍼에서 이미지 데이터를 얻을 수 있다.
우선 텍스춰를 맵핑하는 코드를 작성하기 전에 UIImage를 불러와 텍스춰를 생성하는 클래스를 작성해 보겠습니다. XCode에서 새 로운 클래스 생성을 선택하신 다음 NSObject를 상속받는 OGLTexture 클래스를 생성합니다.
-(void)renderView
{
//: 배경을 검은색으로 지운다
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//: 행렬 모드는 모델뷰 행렬로 변경한다
glMatrixMode(GL_MODELVIEW);
//: 모델뷰 행렬을 초기화한다
glLoadIdentity();
//: 정점배열을 설정한다
glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*5,
verticesForGL_TRIANGLE_STRIP);
//: 텍스춰배열을 설정한다
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat)*5,
&verticesForGL_TRIANGLE_STRIP[0]+3);
//: 텍스춰 배열 사용을 ON
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//: 정점 배열 사용을 ON
glEnableClientState(GL_VERTEX_ARRAY);
{
//: 처리할 정점의 개수는 4개
glEnable(GL_TEXTURE_2D);
[texture bindTexture];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_TEXTURE_2D);
}
//: 정점 배열 사용을 OFF
glDisableClientState(GL_VERTEX_ARRAY);
//: 텍스춰 좌표 배열 사용을 OFF
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
그러면 아래와 같이 텍스춰가 폴리곤에 맵핑되어 출력됩니다.
그런데 그림을 보면 그림이 뒤집혀져 있는 것을 볼 수 있습니다. 그것은 일반이미지를 OpenGL|ES에 맵핑할 때 생기는 현상입니다. PVRTC 압축 이미지는 제대로 출력되지만 우리가 사용한 png파일은 PVRTC 압축파일이 아니기 때문에 OGLTexture의 loadImage에 다음과 같이 CGBitmapContext를 뒤집는 코드를 작성해야 합니다.
-(BOOL)loadUIImage:(NSString *)path
{
UIImage *image = [UIImage imageWithContentsOfFile:path];
if(image == nil)
return NO;
//: 이미지의 정보를 얻어 온다
CGImageRef cgImage = [image CGImage];
width = CGImageGetWidth(cgImage);
height= CGImageGetHeight(cgImage);
//: 이미지의 너비와 높이가 2의 승수인지 살펴봐야 하지만
//: 생략한다
//: 이미지 데이터의 바이트 배열을 만들기 위해서
//: 비트맵 컨텍스트를 만들고 거기에 이미지를 그린다
//: 텍스춰로 사용하는 이미지는 모두 RGBA 4바이트로 가정한다
if(imageData)
free(imageData);
imageData = (GLubyte *)calloc(width*height*4, sizeof(GLubyte));
if(imageData == NULL)
return NO;
//: 이미지를 그릴 비트맵컨텍스트를 생성한다
CGContextRef bitmapContext = CGBitmapContextCreate(imageData,
width,
height,
8,
width*4,
CGImageGetColorSpace(cgImage),
kCGImageAlphaPremultipliedLast);
//: PVRTC 압축 이미지가 아니면 텍스춰이미지가 역상으로 나온다
//: 그것을 바로 잡아 준다
CGContextTranslateCTM (bitmapContext, 0, height);
CGContextScaleCTM (bitmapContext, 1.0, -1.0);
//: 이미지를 비트맵컨텍스트에 그린다
CGContextDrawImage(bitmapContext,
CGRectMake(0, 0, width, height),
cgImage);
//: 비트맵데이터만 필요하므로 컨텍스트는 메모리 해제한다
CGContextRelease(bitmapContext);
return YES;
}
이제 다시 실행하면 아래와 같이 텍스처가 제대로 출력되는 것을 확인할 수 있습니다.
그림만 출력하면 심심하니깐 정점에 색상도 같이 출력해 보겠습니다.
GLfloat verticesForGL_TRIANGLE_STRIP[] = {
0.2, 0.8, 0.0, //v1
1.0, 0.0, 0.0, 1.0, //R
0.0, 1.0, //UV1
0.2, 0.2, 0.0, //v2
0.0, 1.0, 0.0, 1.0, //G
0.0, 0.0, //UV2
0.8, 0.8, 0.0, //v3
0.0, 0.0, 1.0, 1.0, //B
1.0, 1.0, //UV3
0.8, 0.2, 0.0, //v4
1.0, 1.0, 0.0, 1.0, //Y
1.0, 0.0, //UV4
};
-(void)renderView
{
//: 배경을 검은색으로 지운다
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//: 행렬 모드는 모델뷰 행렬로 변경한다
glMatrixMode(GL_MODELVIEW);
//: 모델뷰 행렬을 초기화한다
glLoadIdentity();
//: 정점배열을 설정한다
glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*9,
verticesForGL_TRIANGLE_STRIP);
//: 색상배열을 설정한다
glColorPointer(4, GL_FLOAT, sizeof(GLfloat)*9,
&verticesForGL_TRIANGLE_STRIP[0]+3);
//: 텍스춰배열을 설정한다ㅏ
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat)*9,
&verticesForGL_TRIANGLE_STRIP[0]+7);
//: 색상 칠하기 방법을 설정한다
glShadeModel(GL_SMOOTH);
//: 텍스춰 배열 사용을 ON
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//: 색상 배열 사용을 ON
glEnableClientState(GL_COLOR_ARRAY);
//: 정점 배열 사용을 ON
glEnableClientState(GL_VERTEX_ARRAY);
{
//: 처리할 정점의 개수는 4개
glEnable(GL_TEXTURE_2D);
[texture bindTexture];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_TEXTURE_2D);
}
//: 정점 배열 사용을 OFF
glDisableClientState(GL_VERTEX_ARRAY);
//: 색상 배열 사용을 OFF
glDisableClientState(GL_COLOR_ARRAY);
//: 텍스춰 좌표 배열 사용을 OFF
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
여기서 이번 강좌를 마치고 다음 튜토리얼에서 텍스춰의 UV좌표를 다뤄 보겠습니다. 감사합니다. :)