본문 바로가기

프로그래밍/Android

SurfaceView/GLSurfaceView

쩝.. 프로젝트도 진행 중인데, opengl을 좀 봐달라는 부탁이..

어째 opengl은 만져볼 일이 전혀 없었다.

웬만한건 hollo world는 거쳐보는데, opengl의 hello world 기억은 없다.

물론 이쪽의 hello world는 삼각형 일테지만... 아니 큐브던가? 긁적..

 

그나마 Direct3D ,Unity3D 만져본게 다행~

 

아무튼 단기간에 기본 사항을 살펴보고, 세부 항목을 살펴보기로 해 기본적인것 부터 다시 점검.

쩝.

 

안드로이드는 뷰들이 모여 윈도우로 구성되고, 이 윈도우들은 각각 서피스라는 공간에 표시하게 된다.

이러한 서피스들이 모여 서피스 플링거를 통해 프레임버퍼로 합쳐져 화면에 보여지게 된다. 

안드로이드 api 단에서 이러한 서피스의 핸들링을 위해 지원되는 클래스가 SurfaceView 이다.

 

VideoView 나 GLSurfaceView 역시 SurfaceView를 상속받고 있다.

 

비디오 플레이어 샘플을 보면 SurfaceView를 생성하고, SurfaceHolder 받아 서피스를 컨트롤 하게된다.

SurfaceView도 View에서 나온 녀석이라 기존 View 들과 동일하게 동작한다.

단, holder를 통한 surface 직접 접근이 차이점이다.

 

이는 기존 onDraw 와 같은 UI 쓰레드에서 처리되어지는 작업과 별도로 Surface에 특정 처리가 가능하게 한다.

 

>> SurfaceView

 

1. 서피스 생성 해 뷰에 붙이기

SurfaceView v = new SurfaceView( context );

v.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT,

ViewGroup.LayoutParams_FILL_PARENT );

 

SurfaceHolder sh = v.getHolder();

sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

sh.addCallback( new SurfaceHolder.Callback() {

public void surfaceCreated(SurfaceHolder holder ){

}

 

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height ) {

}


public void surfaceDestroyed(SurfaceHolder holder) {

}

});

 

myLayout.addView( v );

 

이렇게 생성된 SurfaceHolder를  MediaPlayer 같은 곳에 setDisplay(sh); 같이 사용할 있다.

 

2. 서피스에 그림그리기

서피스 홀더에는 서피스의 캔버스를 얻는 메쏘드가 존재한다.

holder.lockCanvas() / holder.lockCanvas( Rect dirty)

 

리턴값은 Canvas 로 위에 언급한바와 같이 이 캔버스는 UI 쓰레드와 분리되어 작업이 가능하다.

쓰레드를 생성해 렌더링 루프를 구성하고, 해당 루틴에서 직접 그려 주면 된다.

 

while( ) {

Canvas c  = null;

c = holder.lockCanvas();

synchronized( holder ) {

myDraw( c );

}

 

holder.unlockCanvasAndPos( c );

}

 

물론 실제 사용될 때에는 시간 간격도 계산해야하고, 루프 조건도 봐야 하고, 다른 입력들에 대한

싱크도 맞춰야 하고.. 할건 많은데.. 어찌되었든 시작은 이렇다.

 

 

 

 

 

 

>> GLSurfaceView : 이름그대로 opengl 을 위한 녀석이다.

요건 정리할게 많으므로 일단 api demo의 샘플만 살펴본다.

GLSurfaceViewActivity.java를 보니 일단은 간단하다.

 

public class GLSurfaceViewActivity extends Activity {

private GLSurfaceview mGLSurfaceView;

 

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

 

mGLSurfaceView = new GLSurfaceview(this);

mGLSurfaceView.setRenderer(new CubeRenderer(false));

setContentView(mGLSurfaceView);

}

 

protected void onResume() {

super.onResume();

mGLSurfaceView.onResume();

}

 

protected void onPause() {

super.onPause();

mGLSurfaceView.onPause();

}

}

 

각 activity의 state에 따라 glsurface 역시 state가 변경되어야 하고, renderer가 필요함을 알수 있다.

 

렌더러를 살펴보면, 역시 단촐하게 구성되어 있다.

class CubeRenderer implements GLSurfaceView.Renderer {

private boolean mTranslucentBackground;

private Cube mCube;

private float mAngle;

 

public CubeRenderer(...) {

}

 

public void onDrawFrame(GL10 gl) {

}

 

public void onSurfaceChanged(GL10 gl, int width, int height) {

 

}

 

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

}

}

 

Cube 클래스는 버텍스와 색상 배열을 포함하고 있는 오브젝트이다.

 

음 하는김에 큐브라도 그리자..

일단 서피스가 생성되면 초기화 루틴이 들어간다.

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

// 속도를 위해 디더링 끄고

gl.glDisable(GL10.GL_DITHER);

 

// 품질 설정

gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

 

gl.glClearColor(r, g, b, a);

 

// cull 모드(후면제거)

gl.glEnable(GL10.GL_CULL_FACE);

 

// 쉐이더 모델

gl.glShadeModel(GL10.GL_SMOOTH);

 

// 깊이버퍼

gl.glEnable(GL10.GL_DEPTH_TEST);

}

 

서피스가 변경되면,

public void onSurfaceChanged(GL10 gl, int width, int height) {

gl.glViewport(0, 0, width, height);

 

// 프로젝션 설정

float ratio = (float) width / height;

gl.glMatrixMode(GL10.GL_PROJECTION);

gl.glLoadIdentity();

gl.glFrustumf( -ratio, ratio, -1, 1, 1, 10 );            

}

 

 

큐브 그리기

public void onDrawFrame( GL10 gl) {

// 색상, 깊이 버퍼 비우기

gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

 

// opengl은 directX랑 다르게 모델/뷰(모델/월드) 변환이 동시에 일어남.

gl.glMatrixMode( GL10.GL_MODELVIEW );

gl.glLoadIdentity();

gl.glTranslatef(0, 0, -3.0f);

gl.glRotate( mAngle, 0, 1, 0 );

gl.glRotate( mAngle*0.25f, 1, 0, 0);

 

gl.glEnableClientState( GL10.GL_VERTEX_ARRAY );

gl.glEnableClientState( GL10.GL_COLOR_ARRAY );

 

// 큐브 그리기

mCube.draw(gl);

 

gl.glRotatef(mAngle*2.0f, 0, 1,1 );

gl.glTranslatef(0.5f,0.5f,0.5f);

 

mCube.draw(gl);

 

mAngle += 1.2f;

}

 

Cube 클래스. 세개의 버퍼를 가지고 있고, 버퍼를 사용해 실제로 큐브를 그린다.

IntBuffer mVertexBuffer;

IntBuffer mColorBuffer;

ByteBuffer mIndexBuffer;

 

// 큐브 버텍스 좌표(x,y,z)

int vertices[] = { -1,-1,-1,    1,-1,-1,  .... };

 

// 버텍스별 색상값

int colors[] = { 0,0,0,1,     1,0,0,1,  ... };

 

// 인텍스

byte indices[] = { 0,4,5,   0,5,1 ... };

 

일반적인 버텍스 배열에 버텍스 위치를 넣는데, 이를 그냥 사용하지 않는다.

버퍼가 opengl에 직접 전달되기 때문에,가비지 컬렉터가 이를 이동시켜서는 안된다.

native heap 영역에 생성하고, 멀티 바이트셋은 각 바이트오더를 native order로 변경해 주어야 한다.

 

ByteBuffer vbb = ByteBuffer.allocatedirect( vertices.length * 4 );

vbb.order( ByteOrder.nativeOrder() );

mVertexBuffer = vbb.asIntBuffer();

mVertexBuffer.put(vertices);

mVertexBuffer.position(0);

 

색상은 버텍스와 동일.

 

인텍스버퍼는 바이트 버퍼이므로 그냥 할당.

mIndexBuffer = ByteBuffer.allocatedirect(indices.length );

mIndexBuffer.put(indices);

mIndexBuffer.position(0);

 

그리는 루틴

public void draw(GL10 gl) {

// 시계방향

gl.glFrontFace(gl.GL_CW);

 

// 한점을 표시하기위한 바이트수와 버퍼 지정

gl.glVertexPointer( 3, gl.GL_FIXED, 0, mVertexBuffer );

gl.glColorPointer( 4, gl.GL_FIXED, 0, mColorBuffer );

 

// 인덱스 지정

gl.glDrawElements( gl.GL_TRIANGLES, 36, gl.GL_UNSIGNED_BYTE, mIndexBuffer );

}

 

 

>> 텍스쳐( TriangleRenderer )

 

* 초기 설정(onSurfaceCreated)

 

gl.glEnable(GL10.GL_TEXTURE_2D);

 

// 텍스쳐 1개를 생성하고, id를 부여받는다.

int[] textures = new int[1];

gl.glGenTextures( 1, textures, 0 );

int id = textures[0];

 

// 바인딩

gl.glBindTexture( GL10.GL_TEXTURE_2D, id );

 

// 텍스쳐 파라미터 설정

gl.glTexParameterf( GL10.GL_TEXTURE_2D,

GL10.GL_TEXTURE_MIN_FILTER,

GL10.GL_NEAREST);

 

gl.glTexParameterf( GL10.GL_TEXTURE_2D,

GL10.GL_TEXTURE_MAG_FILTER,

GL_10.GL_LINEAR );

 

gl.glTexParameterf( GL10.GL_TEXTURE_2D,

GL10.GL_TEXTURE_WRAP_S,

GL10.GL_CLAMP_TO_EDGE);

 

gl.glTexParameterf( GL10.GL_TEXTURE_2D,

GL10.GL_TEXTURE_WRAP_T,

GL10.GL_CLAMP_TO_EDGE);

 

gl.glTexEnvf( GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,

GL10.GL_REPLACE);

 

// BITMAP을 텍스쳐로 변환

GLUtils.texImage2D( GL10.GL_TEXTURE_2D, 0, bitmap, 0 );

bitmap.recycle();

 

* onDrawFrame

gl.glDisable( GL10.GL_DITHER );

gl.glTexEnvx( GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,

GL10.GL_MODULATE);

gl.glClear( GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

 

gl.glMatrixMode( GL10.GL_MODELVIEW );

gl.glLoadIdentity();

 

gl.glEnableClientState( GL10.GL_VERTEX_ARRAY );

gl.glEnableClientState( GL10.GL_TEXTURE_COORD_ARRAY );

gl.glActiveTexture( GL10.GL_TEXTURE0 );

gl.glBindTexture( GL10.GL_TEXTURE_2D, id );

gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,

GL10.GL_REPEAT );

gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,

GL10.GL_REPEAT);

 

mTriangle.draw( gl );

 

* 모델

class Triangle {

private FloatBuffer mFVertexBuffer;

private FloatBuffer mTexBuffer;

private ShortBuffer mIndexBuffer;

 

private final static int VERTS = 3;

 

public Triangle() {

// 버텍스 버퍼 설정( 버텍스수 * 3(x,y,z) * 자료형 크기

ByteBuffer vbb = ByteBuffer.allocateDirect( VERTS * 3 * 4 );

vbb.order( ByteOrder.nativeOrder() );

mFVertexBuffer = vbb.asFloatBuffer();

 

mFVertexBuffer.put( -1.0f );

mFVertexBuffer.put( -0.5f );

mFVertexBuffer.put( 0 );

.

.

.

mFVertexBuffer.position(0);

 

 

// 텍스쳐 coords 좌표

ByteBuffer tbb = ByteBuffer.allocateDirect( VERTS * 2 * 4 );

tbb.order( ByteOrder.nativeOrder());

mTexBuffer = tbb.asFloatBuffer();

 

mTexBuffer.put( 0.5 );

mTexBuffer.put(

.

.

.

mTexBuffer.position(0);

 

 

 

// 인덱스 버퍼

ByteBuffer ibb = ByteBuffer.allocateDirect( VERT * 2 );

ibb.order( ByteOrder.nativeOrder());

mIndexBuffer = ibb.asShortBuffer();

 

mIndexBuffer.put( 0 );

mIndexBuffer.put( 1 );

mIndexBuffer.put( 2 );

.

.

.

mIndexBuffer.position(0);

 

}

 

public void draw( GL10 gl ) {

gl.glFrontFace( GL10.GL_CCW );

gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, mFVertexBuffer );

gl.glEnable( GL10.GL_TEXTURE_2D );

gl.glTexCoordPointer( 2, GL10.GL_FLOAT, 0, mTexBuffer );

gl.glDrawElements( GL10.GL_TRIANGLE_STRIP, VERTS,

GL10.GL_UNSIGNED_SHORT, mIndexBuffer );

}

 

 

>> projection / othographic

gl.glMatrixMode( GL10.GL_PROJECTION );

aspect = width / height;

fovy : 45~60

> projection : gl.glFrustumf( left, right, top, bottom, near, far );

> othographic : gl.glOrhof( left, right, top, bottom, near, far);

> perspective : GLU.gluPerspective( gl, fovy, aspect, near, far);

'프로그래밍 > Android' 카테고리의 다른 글

GLSurfaceView 배경 투명하게  (1) 2011.11.04
최상위 뷰 만들기  (0) 2011.10.27
OpenGL 프레임버퍼 사용  (0) 2011.10.27
ADB tcp/usb 연결설정  (0) 2011.10.26
sdk3.0 fragment를 하위버전에서 사용하기  (0) 2011.10.19
Custom layout, layoutinflater  (0) 2011.10.11
MediaStore 썸네일 데이터 관련 정리  (0) 2011.09.26
뷰의 크기 변경,이동  (0) 2011.09.23
리스트뷰 메쏘드 몇개  (0) 2011.09.22
SQLite Database  (0) 2011.09.20