Android Open GL Lesson #1
안드로이드 Open GL 기본 지식
1. SurfaceView : 안드로이드에서 Surface 라는 것은 GUI 가 그려지는 화면이다. 그냥 이렇게 간단하게 이해하면 된다. View 를 상속 받는 클래스인데 Renderer 라는 인터페이스를 등록해서 3D 작업이 별도의 쓰레드로 돌아가도록 만들어져 있다. 왜 별도로 돌아가도록 하냐면 메인 UI 에서 3D 작업을 같이 처리하려면 시간이 많이 걸려서 키 입력 지연 같은 현상이 발생하기 때문이다.
2. Renderer : Surface create, change, draw 와 같은 3D 에 핵심적인 작업들을 수행한다.
3. GL10 : 현재 안드로이드에서 지원하는 것은 Open GL ES 1.0 이기 때문에 GL10 이라고 하는 것 같다. 안드로이드 문서에 보면 Open GL ES 1.0 은 Open GL 1.3 에 대응(correspond) 한다고 돼 있는 데 무슨 차이점이 있는 지는 모르겠다.
SurfaceView 클래스는 Main UI (우리가 만든 Application) 에서 쓰는 것이고 GL10 관련 메소드들은 Display 를 위한 하드웨어에 필요한 설정을 하는 것이다. 이 두 가지가 연결돼서 3D 기능을 구현할 수 있는 것이다.
일단 프로젝트 시작은 아래와 같이 하면 된다. 이건 공식 같은 거라서 그냥 외우고 시작하는 것이 정신 건강에 좋다. 다만 CustomRenderer 클래스 안에 있는 코드들은 다르게 작성할 수도 있다.
public class AndroidOpenGL extends Activity {
private CustomSurfaceView mSurfaceView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSurfaceView = new CustomSurfaceView(this);
setContentView(mSurfaceView);
}
@Override
protected void onPause() {
mSurfaceView.onPause();
super.onPause();
}
@Override
protected void onResume() {
mSurfaceView.onResume();
super.onResume();
}
}
public class CustomSurfaceView extends GLSurfaceView {
private CustomRenderer mRenderer;
public CustomSurfaceView(Context context) {
super(context);
mRenderer = new CustomRenderer(this);
setRenderer(mRenderer);
}
}
public class CustomRenderer implements Renderer {
public CustomRenderer(CustomSurfaceView view) {
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, -6.0f);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, (float) width / height, 1.0f, 30.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
}
onSurfaceChanged() : 화면 사이즈가 변경되는 등 어떤 변화가 생길 때 실행된다. 화면 변화가 없더라도 클래스가 처음 생성될 때도 실행되기 때문에 필요한 몇 가지 gl 메소드들이 들어가 있다.
gl.glViewport(0, 0, width, height); 휴대폰 화면에 보이는 영역을 정한다. width, height 값을 찍이 보진 않았는데 전체 화면으로 나오는 걸로 봐서 Title 영역 제외한 전체 영역이 기본 설정되는 것 같다.
gl.glMatrixMode(GL10.GL_PROJECTION); 3D 좌표를 2D 스크린 좌표로 변환하는 방식. 3D 영상이든 물체든 어쨋든 컴퓨터 모니터나 휴대폰 스크린에 보이기 위해서는 2D 평면에 그려야 한다. 이것을 투영(Projection) 이라고 하는데 회전이나 이동을 어떻게 할지 계산할 때 쓰는 행렬이 있다. 직교 투영은 물체의 거리에 상관 없이 크기가 변하지 않는 것이고 원근 투영은 멀리 있는 것은 작게 보이고 가까이 있는 것은 크게 보이도록 투영하는 방식이다.
gl.glLoadIdentity(); 변환 행렬을 초기화하는 것이다. 변환 행렬은 보통 4*4 배열인데 회전이나 이동을 한 다음 이 메소드를 실행하면 변환 행렬이 초기화 된다. 즉 행렬안에 요소 중에 대각선 부분에 있는 값들만 1 이고 나머지는 모두 0 으로 바뀐다. 바로 위에서 gl.glMatrixMode(GL10.GL_PROJECTION) 다음에 이 메소드를 실행했기 때문에 투영행렬을 초기화 한다는 뜻이다.
GLU.gluPerspective(gl, 45.0f, (float) width / height, 1.0f, 30.0f); 원근 투영 방식을 쓰겠다는 뜻이다. 그리고 Window 의 Aspect ratio 를 계산해서 Opengl 쪽에 알려 준다. 화면의 가로/세로 비율과 세로 시야각, 화면 깊이 등을 설정하는 메소드이다. 45.0f 는 Y 축 방향으로 보이는 각을 의미하는 데 사람 눈이 상하 45 도 정도만 볼 수 있어서 45.0 으로 설정한 것인 지.. 마지막 매개 변수는 각각 가까운 z 좌표, 먼 z 좌표 값인데 1.0, 30.0 이라는 값은 화면에 보이는 가장 가까운 쪽은 1 만큼 떨어져 있고 가장 먼쪽은 30 만큼 떨어져 있다는 뜻이다. 인터넷에서 Opengl 로 검색하면 절두체라는 그림과 함께 자세한 설명이 있다.
gl.glMatrixMode(GL10.GL_MODELVIEW); Model View 라는 것은 3D 공간 상에 위치한 모델(객체? 물체?)가 회전이나 이동하는 것을 말한다. 이 메소드를 호출하면 이런 물체들의 이동과 회전을 계산할 때 쓰는 행렬을 로딩했다는 뜻이다.
gl.glLoadIdentity(); 투영행렬을 쓰겠다고 gl.glMatrixMode(GL10.GL_PROJECTION) 실행하고 행렬 초기화를 했듯이 모델의 변환 행렬을 쓰겠다고 gl.glMatrixMode(GL10.GL_MODELVIEW) 를 호출하면 초기화를 해 줘야 하기 때문에 이 메소드를 실행해준다.
onSurfaceCreated():OpenGL 에 대한 모든 초기화 과정을 수행하는 메소드. 어떤 컬러 값으로 화면 배경을 칠할 것인 지, depth buffer 를 사용할 것인지, smooth shading 을 사용할 것인지 등등..
gl.glShadeModel(GL10.GL_SMOOTH); 다면체를 그릴 때 컬러값들이 잘 혼합되서 보일 수 있도록 설정. 삼각형 꼭지점에 각각 색을 지정하고 그리면 중간 지점에 컬러 값들이 이쁘게 섞이는 것을 볼 수 있다.
다른 강의에서 더 자세한 설명할 것이다.
gl.glClearColor(0.5f, 0.5f, 0.5f, 0.5f); 화면 배경색 설정. 각각 Red, Green, Blue, 투명도를 의미한다. 값의 범위는 0 ~ 1.0f. 0에 가까울수록 어두운 색이고 1 에 가까울수록 밝은 색이 된다.
gl.glClearDepthf(1.0f); Depth Buffer 설정. 모든 픽셀은 깊이(z) 값을 갖는다. 이것을 저장하기 위한 버퍼가Depth buffer 이고 이것을 clear 한다는 의미다.
gl.glEnable(GL10.GL_DEPTH_TEST); Depth 값 비교 가능하도록 설정. 이게 Enable 상태가 돼야 아래 코드가 동작한다.
gl.glDepthFunc(GL10.GL_LEQUAL); 다면체를 그릴 때 깊이가 작은 것만 그린다는 의미로 시야에서 먼 쪽에 있어서 보이지 않는 면은 그리지 않도록 하는 것이다.
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); we want the best perspective correction to be done. This causes a very tiny performance hit, but makes the perspective view look a bit better. 무슨 뜻인지…
public void onDrawFrame(GL10 gl) : 드디어 화면에 실제로 그리는 부분이다.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 화면을 깨끗이 한다는 의미. Depth buffer 는 3D 이기 때문에 필요한 것이다. 평면이 아니기 때문에 화면의 픽셀이 어떤 깊이 값을 갖고 있는 지 저장하는 버퍼가 있는 데 이것을 초기화 하는 것이다.
gl.glLoadIdentity(); 현재의 ModelView matrix 를 초기화 하는 과정. 일단 좌표계를 초기화 한다고 생각하면 될 것 같다.
gl.glTranslatef(0.0f, 0.0f, -6.0f); 좌표계를 이동시킨다. x, y 좌표는 그대로 두고 z 축만 이동시킨다는 의미다. 다음 강의에서 삼각형을 그려 볼 것인데 이 때 z 축을 0.0f 로 해 놓고 실행하면 아무것도 안 보일 것이다. 왜냐면 z 좌표가 0 이라는 것은 눈의 위치와 똑같다는 의미다. 즉, 우리는 눈의 앞에 있는 것은 볼 수 있지만 눈과 같은 위치에 있거나 뒤 쪽에 있는 것은 볼 수 없는 것과 같은 이치. 휴대폰 화면에서 x 좌표는 가로 축, y 는 세로 축, z 는 화면의 깊이이기 때문에 z 좌표에 – 값을 줘야지 비로소 화면에 그려지는 도형을 눈으로 볼 수 있는 것이다.
여기까지 코드 작성하고 실행하면 전체 화면이 회색 비슷한 색으로 칠해 질 것이다.
[출처] Android Open GL #1|작성자 이외수와베르나르