|
텍스쳐 재료로 사용될 이미지는 기본적으로 2의 제곱수 이여야합니다. 2의 제곱수가 아니라면 텍스쳐가 정상출력되지 않습니다.
또한 이미지의 크기는 최대 1024 * 1024까지 사용할 수 있습니다. 가로와 세로는 같을필요가 없고 가로와 세로 모두 2의 제곱수이면 됩니다.
2의 제곱수인 이미지를 한 장 준비합시다. 이 팬더이미지는 512*512 입니다.
1. 자바에서 이미지 데이터 보내기
이미지를 drawable폴더에 넣고 GLView.java를 보겠습니다.
package pe.berabue.opengl; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView.Renderer; import android.view.MotionEvent; public class GLView extends GLSurfaceView implements Renderer { private Context mContext; private static native void nativeCreated(); private static native void nativeChanged(int w, int h); private static native void nativeUpdateGame(); private static native void nativeOnTouchEvent(int x, int y, int touchFlag); private static native void nativeSetTextureData(int[] pixels, int width, int height); public GLView(Context context) { super(context); this.setRenderer(this); this.requestFocus(); this.setRenderMode(RENDERMODE_WHEN_DIRTY); this.setFocusableInTouchMode(true); mContext = context; } public void onDrawFrame(GL10 gl) { nativeUpdateGame(); } public void onSurfaceChanged(GL10 gl, int w, int h) { nativeChanged(w, h); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeCreated(); Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.panda); int[] pixels = new int[bmp.getWidth()*bmp.getHeight()]; bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight()); nativeSetTextureData(pixels, bmp.getWidth(), bmp.getHeight()); } @Override public boolean onTouchEvent(MotionEvent event) { nativeOnTouchEvent((int)event.getX(), (int)event.getY(), event.getAction()); return true; } }비트맵을 사용하기위한 Context와 텍스쳐데이터를 보내기위한 nativeSetTextureData();
그리고 onSurfaceCreated() 부분의 코드가 추가되었습니다.
가장 중요한 onSurfaceCreated(); 부분을 보겠습니다.
Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.panda);
위에서 저장한 panda이미지를 불러옵니다.
int[] pixels = new int[bmp.getWidth()*bmp.getHeight()];
픽셀데이터를 담을수 있도록 위 이미지의 가로*세로 크기의 int배열을 생성합니다.
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
해당 이미지의 픽셀데이터를 읽어와 배열에 저장합니다.
nativeSetTextureData(pixels, bmp.getWidth(), bmp.getHeight());
해당 픽셀데이터, 가로, 세로 크기를 전달합니다 ~
2. jni-ndk.c 에서 받기
void Java_pe_berabue_opengl_GLView_nativeSetTextureData(JNIEnv* env, jobject thiz, jintArray arr, jint width, jint height) { char *data = (*env)->GetByteArrayElements(env, arr, 0); setTextureData(data, width, height); (*env)->ReleaseByteArrayElements(env, (jbyteArray)arr, data, JNI_ABORT); }char *data = (*env)->GetByteArrayElements(env, arr, 0);
자바에서 넘어온 배열을 통째로 복사합니다.
setTextureData(data, width, height);
픽셀데이터와, 이미지의 가로, 세로 크기를 보내버리고 ~
(*env)->ReleaseByteArrayElements(env, (jbyteArray)arr, data, JNI_ABORT);
자바에서 넘어온 배열을 해제시켜줍니다!
JNI 부분은 아직도 이해가안되서 사용하기가 어렵네요... 대충 기능만 적었습니다..ㅜㅜ
3. GLGameRenderer.c에서 나머지를 해결해봅시다.
먼저 변수 몇개가 필요할것같군요.
GLuint g_textureName; int g_nX; int g_nY; int g_nPandaWidth; int g_nPandaHeight;g_textureName;
각각의 텍스쳐마다 이름을 지정해주어 해당텍스쳐를 불러내는 용도입니다.
텍스쳐의의 시작점(LT기준)과 텍스쳐 크기에 사용될 변수가 있습니다. ( 적당히 초기화를 해주시면됩니다. )
void setTextureData(char *data, int width, int height) { g_nPandaWidth = width; g_nPandaHeight = height; glGenTextures(1, &g_textureName); glBindTexture(GL_TEXTURE_2D, g_textureName); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)data); }텍스쳐를 만들어내는 곳입니다.
glGenTextures(1, &g_textureName);
void glGenTextured(GLsizei n, GLuint *텍스쳐);
OpenGL에서는 텍스쳐에 각각의 번호를 매겨사용을합니다.
생성할 텍스쳐의 수(n)와 텍스쳐의 이름(번호)으로 사용될 배열을 넘겨주면 배열에 값이 들어옵니다.
glBindTexture(GL_TEXTURE_2D, g_textureName);
사용할 텍스쳐를 선택합니다. 2차원이면서 g_textureName의 이름(번호)를 가진 텍스쳐를 사용할겁니다.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
축소시에 부드럽게
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
확대시에 픽셀을 유지
위 두 함수의대한 설명은 http://mrhoya.tistory.com/entry/Filtering-MIPMAP%EC%9D%98-%EC%9D%B4%ED%95%B4
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,(void*)data);
glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height
, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
target : 2차원 텍스쳐를 생성합니다.
level : 디테일 레벨, 밉맵핑을 사용하지 않으면 보통 0
internalformat : 이미지 데이터의 내부 포맷
width : 텍스쳐 가로폭
height : 텍스쳐 세로폭
border : 테두리두께로 0 ~ 2 인듯싶습니다(?) 보통 0
format : 픽셀 데이터의 포맷
pixels : 픽셀 데이터
텍스쳐를 생성했으니 그려봅니다!
void updateGameLoop() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawPanda(); } void drawPanda() { GLfloat vertices[12] = { g_nX , g_nY+g_nPandaHeight , 0.0f, // LEFT | BOTTOM g_nX+g_nPandaWidth , g_nY+g_nPandaHeight , 0.0f, // RIGHT | BOTTOM g_nX , g_nY , 0.0f, // LEFT | TOP g_nX+g_nPandaWidth , g_nY , 0.0f // RIGHT | TOP }; GLfloat texture[8] = { 0 , 1, 1 , 1, 0 , 0, 1 , 0 }; glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, g_textureName); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, texture); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_TEXTURE_2D); }drawPanda(); 팬더를 그립니다 ~
vertices
텍스쳐가 그려질 좌표(정점)입니다.
texture
그려질 텍스쳐의 영역입니다. 0 ~ 1까지 값이 들어가며 0 ~ 100%로 보시면됩니다.
위 배열에서 왼쪽은 w이며, 오른쪽은 h입니다.
만약, 이미지의 좌상단 1/4만을 출력하고싶다면 1을 0.5로 바꾸어주면 됩니다.
glEnable(GL_TEXTURE_2D);
2D텍스쳐를 활성화시킵니다.
glBindTexture(GL_TEXTURE_2D, g_textureName);
바로 위에서 만들어주었던 이름(번호)과 동일한 텍스쳐를 그릴것이구요~
glEnableClientState(GL_VERTEX_ARRAY);
해당 기능 활성화시 glVertexPointer에 설정한 vertices를 참고하여 렌더링
glVertexPointer(3, GL_FLOAT, 0, vertices);
버텍스 데이터 좌표배열을 지정
3 : 좌표 당 버텍스 수. 2~4
GL_FLOAT : 타입
0 : 배열 내의 버텍스 간격. ( 여기서는 배열하나의 여러개의 좌표를 갖고있지 않으므로 0 )
vertices : 데이터 포인터
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
해당 기능 활성화시 glTexCoordPointer 에 설정한 texture 를 참고하여 렌더링
glTexCoordPointer(2, GL_FLOAT, 0, texture);
텍스쳐 좌표 배열을 지정
2 : 배열당 좌표 수 1 ~ 4
GL_FLOAT : 타입
0 : 배열 내의 좌표 간격
texture : 데이터 포인트
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GL_TRIANGLE_STRIP(연속 삼각형) : 해당 모양으로 그리기
0 : 배열의 첫번째 인덱스
4 : 사용할 인덱스 수
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
비활성화
여기까지! 입니다!!
빌드 후 실행해보면 ~ 짜잔!!
으헉... 빼먹은게 있습니다.
자바에서 픽셀데이터를 보내왔을때 데이터의 색상순서가 RGBA가 아닌 BGRA순서입니다.
그럼, 순서를 바꾸어줍시다~!
void setTextureData(char *data, int width, int height) { int i; char *buf; buf = (char *)malloc((sizeof(char)*width*height)<<2); for (i = 0; i < width*height*4; i += 4) { buf[i] = data[i+2]; buf[i+1] = data[i+1]; buf[i+2] = data[i]; buf[i+3] = data[i+3]; } g_nPandaWidth = width; g_nPandaHeight = height; glGenTextures(1, &g_textureName); glBindTexture(GL_TEXTURE_2D, g_textureName); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)buf); free(buf); }변수 두개와 메모리할당, for문, 그리고 아래쪽에 메모리를 해제시켜주는 부분이 추가되었습니다.
픽셀데이터와 같은 크기의 메모리를 할당받고 BGRA -> RGBA순서로 변경해줍니다.
glTexImage2D() 마지막 값이 data -> buf로 바뀌었내요.
다시 빌드 후 실행해보면 ~
정상출력되었습니다!
위의 vertices와 texture 배열을 이용하면
원하는 위치를 중심으로 출력하거나, 좌표, 그려질 영역등을 마음대로 조절할 수 있습니다.
또한 값의 순서를 바꾸면 이미지가 정상출력이 되지 않습니다.
OpenGL은 모든 도형을 삼각형을 합쳐서 그리기때문에 정점의 순서를 맞추어주지 않으면 텍스쳐가 제대로 나오지 않습니다~
한번해보세요 ~
오늘은 여기까지 ~
|
'Android > OpenGL' 카테고리의 다른 글
[NDK-OpenGL_05] 2의 제곱수가 아닌 텍스쳐 출력하기 (0) | 2012.03.06 |
---|---|
[NDK-OpenGL_04] 쓰레드 생성 (0) | 2012.03.04 |
[NDK-OpenGL_02] 해상도 지정하기 (3) | 2012.02.26 |
[NDK-OpenGL_01] 프로젝트 준비 (3) | 2012.02.26 |
[ Android OpenGL - 04 ] 기본 소스 정리 (0) | 2011.05.20 |