본문 바로가기

카테고리 없음

안드로이드 OpenGL ES 예제, 텍스쳐 육면체

안드로이드에서 육면체를 구성하고 육면체의 각 면에 텍스쳐를 적용하는 예

개요

안드로이드 OpenGL ES에서 육면체에 텍스쳐를 적용할 때, 각 면에 해당하는 정점좌표 4개가 준비되어야 한다. 각 면은 인접한 면과 정점을 공유하고 있지만 한개의 정점이 2개의 텍스쳐 좌표와 매핑될 수는 없기 때문에 각 면마다 4개의 정점이 정점버퍼에 명시적으로 선언되어야 한다. 그러므로 육면체의 각면을 표현하는 4개의 좌표가 필요하고 6면에 4개의 정점좌표를 선언해야 하므로 총 24개의 정점좌표가 필요하다.

텍스쳐 좌표의 배열을 생성할 때는 정점배열의 순서를 참고하여 해당 정점에 매핑할 텍스쳐의 위치를 텍스쳐 좌표로 설정하면 된다.

텍스쳐로 사용된 이미지 


Activity

package gl.test6;


import android.app.Activity;

import android.opengl.GLSurfaceView;

import android.os.Bundle;

import android.view.Window;

import android.view.WindowManager;


public class TexturedCubeActivity extends Activity {


@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);


// Remove the title bar from the window.

this.requestWindowFeature(Window.FEATURE_NO_TITLE);


// Make the windows into full screen mode.

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);


// Create a OpenGL view.

GLSurfaceView view = new GLSurfaceView(this);


// Creating and attaching the renderer.

TexturedCubeRenderer  renderer = new TexturedCubeRenderer(this);

view.setRenderer(renderer);

setContentView(view);

}

}


Renderer

package gl.test6;


import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;

import android.content.*;

import android.graphics.*;

import android.opengl.GLSurfaceView.Renderer;

import android.opengl.GLU;


public class  TexturedCubeRenderer implements Renderer {

private Context context;

private TexturedCube cube;

public  TexturedCubeRenderer (Context context) {

this.context = context;

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.crate3);

cube = new TexturedCube(bitmap);

}


public void onSurfaceCreated(GL10 gl, EGLConfig config) {

// Set the background color to black ( rgba ).

gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

// Enable Smooth Shading, default not really needed.

gl.glShadeModel(GL10.GL_SMOOTH);

// Depth buffer setup.

gl.glClearDepthf(1.0f);

// Enables depth testing.

gl.glEnable(GL10.GL_DEPTH_TEST);

// The type of depth testing to do.

gl.glDepthFunc(GL10.GL_LEQUAL);

// Really nice perspective calculations.

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

}


float angle;

public void onDrawFrame(GL10 gl) {

// Clears the screen and depth buffer.

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

// Replace the current matrix with the identity matrix

gl.glLoadIdentity();

// Translates 4 units into the screen.

gl.glTranslatef(0, -2, -10);

gl.glRotatef(angle, 0, 1, 0);

// Draw our scene.

cube.draw(gl);

angle += 5;

}


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

// Sets the current view port to the new size.

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

// Select the projection matrix

gl.glMatrixMode(GL10.GL_PROJECTION);

// Reset the projection matrix

gl.glLoadIdentity();

// Calculate the aspect ratio of the window

GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 1000.0f);

// Select the modelview matrix

gl.glMatrixMode(GL10.GL_MODELVIEW);

// Reset the modelview matrix

gl.glLoadIdentity();

}

TexturedCube

package gl.test6;


import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;

import android.opengl.GLUtils;


public class TexturedCube {


// 육면체를 구성하는 6개의 면을 정점(24개)으로 표현한다

float [] vertices = {

     // 앞면

            -1.0f, -1.0f, 1.0f, // 왼쪽 아래 정점

            1.0f, -1.0f, 1.0f,  // 오른쪽 아래

            -1.0f, 1.0f, 1.0f,  // 왼쪽 위

            1.0f, 1.0f, 1.0f,   // 오른쪽 위

            // 오른쪽 면

            1.0f, -1.0f, 1.0f,  // 왼쪽 아래

            1.0f, -1.0f, -1.0f, // 오른쪽 아래         

            1.0f, 1.0f, 1.0f,   // 왼쪽 위

            1.0f, 1.0f, -1.0f,  // 오른쪽 위

            // 뒷면

            1.0f, -1.0f, -1.0f,

            -1.0f, -1.0f, -1.0f,            

            1.0f, 1.0f, -1.0f,

            -1.0f, 1.0f, -1.0f,

            // 왼쪽면

            -1.0f, -1.0f, -1.0f,

            -1.0f, -1.0f, 1.0f,         

            -1.0f, 1.0f, -1.0f,

            -1.0f, 1.0f, 1.0f,

            // 아래쪽 면

            -1.0f, -1.0f, -1.0f,

            1.0f, -1.0f, -1.0f,         

            -1.0f, -1.0f, 1.0f,

            1.0f, -1.0f, 1.0f,

            // 위쪽면

            -1.0f, 1.0f, 1.0f,

            1.0f, 1.0f, 1.0f,           

            -1.0f, 1.0f, -1.0f,

            1.0f, 1.0f, -1.0f,

};

// 36개의 정점을 이용하여 12개의 3각형을 구성한다

short [] indices = {

   //정점배열의 정점 인덱스를 이용하여 각 면마다 2개의 3각형(CCW)을 구성한다

            0,1,3, 0,3,2,           //앞면을 구성하는 2개의 3각형

            4,5,7, 4,7,6,           //오른쪽면 

            8,9,11, 8,11,10,        //... 

            12,13,15, 12,15,14,     

            16,17,19, 16,19,18,     

            20,21,23, 20,23,22,

};


// 정점배열에 선언된 정점의 위치에 텍스쳐 좌표를 배정한다. 해당 정점의 위치에 매핑할 텍스쳐 좌료를 선언하면 된다.

        // 인덱스 배열은 참고할 필요가 없고 정점배열의 정점 순서에 따라서 텍스쳐의 위치를 결정하는 것이 관건이다.

private float [] textures = {

//6개의 면에 매핑될 텍스쳐 좌표 24개를  선언한다

    0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

            

            0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

            

            0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

            

            0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

            

            0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

            

            0.0f, 1.0f,

            1.0f, 1.0f,

            0.0f, 0.0f,

            1.0f, 0.0f,

};

// Our vertex buffer.

private FloatBuffer vertexBuffer;


// Our index buffer.

private ShortBuffer indexBuffer;

// Our UV texture buffer.

private FloatBuffer textureBuffer;


// Our texture id.

private int textureId = -1;


// The bitmap we want to load as a texture.

private Bitmap bitmap;

public TexturedCube(Bitmap bitmap) {

// a float is 4 bytes, therefore we multiply the number if 

// vertices with 4.

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

vbb.order(ByteOrder.nativeOrder());

vertexBuffer = vbb.asFloatBuffer();

vertexBuffer.put(vertices);

vertexBuffer.position(0);

// short is 2 bytes, therefore we multiply the number if 

// vertices with 2.

ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);

ibb.order(ByteOrder.nativeOrder());

indexBuffer = ibb.asShortBuffer();

indexBuffer.put(indices);

indexBuffer.position(0);

ByteBuffer tbb = ByteBuffer.allocateDirect(vertices.length * 4);

tbb.order(ByteOrder.nativeOrder());

textureBuffer = tbb.asFloatBuffer();

textureBuffer.put(textures);

textureBuffer.position(0);

this.bitmap = bitmap;

}

public void draw(GL10 gl) {

// Counter-clockwise winding.

gl.glFrontFace(GL10.GL_CCW);

// Enable face culling.

gl.glEnable(GL10.GL_CULL_FACE);

// What faces to remove with the face culling.

gl.glCullFace(GL10.GL_BACK);

// Enabled the vertices buffer for writing and to be used during 

// rendering.

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

// Specifies the location and data format of an array of vertex

// coordinates to use when rendering.

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

// 텍스쳐 관련 내용

gl.glEnable(GL10.GL_TEXTURE_2D);

// Enable the texture state

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);


if(textureId==-1) loadGLTexture(gl);

// Point to our buffers

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

/*

gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, 

GL10.GL_UNSIGNED_SHORT, indexBuffer);

*/

               // 6개의 면을 구분하여 그리면서 텍스쳐를 적용한다

for(int i=0; i<6; ++i)

{

   gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);// 텍스쳐가 여러개라면 textureid를 루프마다 변경해준다

   indexBuffer.position(6*i);//한 면이 시작되는 인덱스를 선택한다 한면이 6개의 정점으로 구성되므로 6을 곱한다

   //6개의 인덱스를 이용하여 2개의 삼각형(한 면)을 그린다

   gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_SHORT, indexBuffer);

}

// Disable the vertices buffer.

gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

gl.glDisable(GL10.GL_TEXTURE_2D);

// Disable face culling.

gl.glDisable(GL10.GL_CULL_FACE);

}

private void loadGLTexture(GL10 gl) {

// Generate one texture pointer...

int[] textures = new int[1];

gl.glGenTextures(1, textures, 0);

textureId = textures[0];

gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);

// Create Nearest Filtered Texture

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,

GL10.GL_LINEAR);

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

/*

// Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE

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_REPEAT);

*/

// Use the Android GLUtils to specify a two-dimensional texture image

// from our bitmap

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

}

}