openGL ES 를 사용해 보자. 안드로이드에서 API 를 제공한다. GLSurfaceView 라는 모양으로 제공한다.
GLSurfaceView
간단한 GLSurfaceView 를 이용한 application 을 만들어보자. 예제는 ref. 1을 참고했다.GLSurfaceView 사용
public class ClearActivity extends Activity { private GLSurfaceView mGLView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLView = new GLSurfaceView(this); mGLView.setRenderer(new ClearRenderer()); setContentView(mGLView); } @Override protected void onPause() { super.onPause(); mGLView.onPause(); } @Override protected void onResume() { super.onResume(); mGLView.onResume(); } }
class ClearRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Do nothing special. } public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } }
- onSurfaceCreated() :
rendering(렌더링) 을 시작할 때와 OpenGL ES drawing 컨텍스트가 다시 만들어져야 할 때 호출된다. - onSurfaceChanged() :
surface 의 size 가 바뀔 때 호출된다. 여기서 OpneGL viewport 를 설정하면 된다.
scene 주위를 움직이지 않는 fixed camera 라면, camera 도 여기서 set 하면 된다. - onDrawFrame() :
모든 frame 마다 이 함수가 호출된다. 이 함수에서 scene 을 그린다.
일반적으로 시작할 때 framebuffer 를 clear 하기 위해 glClear 를 호출한다. 그리고 그 뒤로 현재 scene 을 그리기 위한 다른 OpenGl ES 호출들이 따라온다.
User Input 을 위한 GLSurfaceView 상속
User Input 을 받기 위해서는 GLSurfaceView 를 상속해야 한다. 소스를 보면 어떻게 해야 할 지 알것이다. 실제 draw 부분은 renderer 에서 작업해 주고, GLSurfaceView 에서는 event 를 날려주기만 하면 된다.
queueEvent
event 를 renderer thread 에게 던져주는 함수이다. renderer thread 에게 event 를 주는 것이기 때문에 renderer 가 set 되어 있어야 한다.
queueEvent
event 를 renderer thread 에게 던져주는 함수이다. renderer thread 에게 event 를 주는 것이기 때문에 renderer 가 set 되어 있어야 한다.
class ClearGLSurfaceView extends GLSurfaceView { public ClearGLSurfaceView(Context context) { super(context); mRenderer = new ClearRenderer(); setRenderer(mRenderer); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } public boolean onTouchEvent(final MotionEvent event) { queueEvent(new Runnable(){ public void run() { mRenderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f); requestRender(); }}); return true; } ClearRenderer mRenderer; } class ClearRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Do nothing special. } public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } public void onDrawFrame(GL10 gl) { gl.glClearColor(mRed, mGreen, mBlue, 1.0f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } public void setColor(float r, float g, float b) { mRed = r; mGreen = g; mBlue = b; } private float mRed; private float mGreen; private float mBlue; }
Activity
GLSurfaceView 에서 Renderer 를 set 해주기 때문에, Activity 에서는 GLSurfaceView 만 set 해주면 된다.@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new ClearGLSurfaceView(this);
setContentView(mGLView);
}
Fragment
Fragment 에서는 아래처럼 해주면 된다.@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View fragment = inflater.inflate(R.layout.main_fragment, container, false); mGLView = new ClearGLSurfaceView(fragment.getContext()); return mGLView; }
GLThread in GLSurfaceView.java
- GLSurfaceView 는 Window 에 attach 될 때 GLThread 를 만들게 된다.
- 이 GLThread 가 renderer thread 인데 이 thread 가 guardedRun() 을 호출 한다.
- 이 gurdedRun() 이 무한 루프를 돌면서 mEventQueue 에 있는 event 들을 run 시킨다.
@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); ... if (mDetached && (mRenderer != null)) { ... mGLThread = new GLThread(mThisWeakRef); if (renderMode != RENDERMODE_CONTINUOUSLY) { mGLThread.setRenderMode(renderMode); } mGLThread.start(); } mDetached = false; } //////////////////////////// // inner static class //////////////////////////// static class GLThread extends Thread { ... @Override public void run() { setName("GLThread " + getId()); ... try { guardedRun(); } catch (InterruptedException e) { // fall thru and exit normally } finally { sGLThreadManager.threadExiting(this); } } }
@Override protected void onDetachedFromWindow() { ... if (mGLThread != null) { mGLThread.requestExitAndWait(); } mDetached = true; super.onDetachedFromWindow(); }
private ArrayListmEventQueue = new ArrayList (); public void queueEvent(Runnable r) { if (r == null) { throw new IllegalArgumentException("r must not be null"); } synchronized(sGLThreadManager) { mEventQueue.add(r); sGLThreadManager.notifyAll(); } } private void guardedRun() throws InterruptedException { while (true) { synchronized (sGLThreadManager) { while (true) { if (mShouldExit) { return; } if (! mEventQueue.isEmpty()) { event = mEventQueue.remove(0); break; } ... } } // end of synchronized(sGLThreadManager) if (event != null) { event.run(); event = null; continue; } } }
RENDERMODE_WHEN_DIRTY
3D 게임같은 프로그램에서는 계속해서 화면을 모두 다 그려야 줄 필요가 있지만, 일반적인 어플리케이션에서는 굳이 그렇게 할 필요가 없다. 그저 변경된 부분만 다시 그려주면 된다. 이것은 아래처럼 render mode 를 WHEN_DIRTY 로 설정 해 주면 된다.- GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY),
그리고 나서, 필요한 곳에서 rendering 을 요청하면 된다. rendering 의 요청은 아래로 가능하다.
- GLSurfaceView.requestRender()
glFrontFace(GL10.GL_CW)
GL_CW 는 clockwise, GL_CCW 는 count-clockwise 이다. 이 방향이 앞면 폴리곤(front-facing polygon) 의 방향이 된다. (Specifies the orientation of front-facing polygons)
아래같이 vertex 가 4개 있다고 하자. 이때 vertex 의 순서를 glDrawElements() 로 넘겨줘서 polygon 을 그리게 한다.(IndexBuffer 를 만드는 과정은 일단 생략한다.)
- byte indices[] = { 1, 2, 3, 2, 4, 3 }
- gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
이 때
glFrontFace(GL10.GL_CW)
라면, 오른쪽 방향의 순서대로 vertex 들의 순서를 정하면, 그 순서로 만들어진 polygon 이 정면이 된다. 만약 순서를 vertex 순서를 시계반대방향(왼쪽)으로 순서를 정하면 그 polygon 은 뒷면이 된다.
예를 들어 만약 indices 를 아래처럼 set 하면, 아마 반쪽짜리 삼각형 polygon 을 볼 것이다. 그 이유는 1 -> 2 -> 3 은 정상적으로 삼각형을 보여줄 테지만, 3 -> 4 -> 2 는 한바퀴 돌려야 보인다.
- byte indices[] = { 1, 2, 3, 3, 4, 2 }
테스트는 Other example 의 Touch Rotate 로 해보면 이해가 갈 것이다.
Other examples
ApiDemo 에 보면 예제들이 더 있다.- <Android_sdk>\samples\android-19\legacy\ApiDemos\src\com\example\android\apis\graphics\
- GLSurfaceView - a spinning triangle
- Kube - a cube puzzle demo
- Translucent GLSurfaceView - shows how to display 3D graphics on a translucent background
- Textured Triangle - shows how to draw a textured 3D triangle
- Sprite Text - shows how to draw text into a texture and then composite it into a 3D scene
- Touch Rotate - shows how to rotate a 3D object in response to user input.
GLSurfaceView 에 대한 좀 더 많은 정보는 ref. 1 을 참고하자.
See Also
- How to capture the app's screen
How to programatically take a screenshot on Android?, StackOverflow
댓글 없음:
댓글 쓰기