안드로이드 OpenGL ES 2에서 사각형에 회전변환을 적용한 Shader 프로그램의 예
이 예제를 안드로이드 기기에서 실행하면 다음과 같이 계속 회전하는 사각형을 확인할 수 있다.
Activity class
package gl.test1;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;
public class OpenGLES2SquareActivity extends Activity {
private GLSurfaceView mGLSurfaceView;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
// Check if the system supports OpenGL ES 2.0.
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2)
{
// Request an OpenGL ES 2.0 compatible context.
mGLSurfaceView.setEGLContextClientVersion(2);
// Set the renderer to our demo renderer, defined below.
mGLSurfaceView.setRenderer(new SquareRenderer());
}
else
{
// This is where you could create an OpenGL ES 1.x compatible
// renderer if you wanted to support both ES 1 and ES 2.
return;
}
setContentView(mGLSurfaceView);
}
@Override
protected void onResume()
{
// The activity must call the GL surface view's onResume() on activity onResume().
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause()
{
// The activity must call the GL surface view's onPause() on activity onPause().
super.onPause();
mGLSurfaceView.onPause();
}
}
SquareRenderer
package gl.test1;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.opengl.GLSurfaceView.Renderer;
public class SquareRenderer implements Renderer {
public float mAngle;
private int mProgram;
private int maPositionHandle;
private int maColorHandle;
private FloatBuffer vbuf, cbuf;
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mMMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 aPosition; \n" +
"attribute vec4 aColor; \n" +
"varying vec4 vColor; \n" +
"void main(){ \n" +
" gl_Position = uMVPMatrix * aPosition; \n" +
" vColor = aColor; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"varying vec4 vColor; \n" +
"void main(){ \n" +
//" gl_FragColor = vec4 (0, 0.5, 0, 1.0); \n" +
"gl_FragColor = vColor; \n" +
"} \n";
@Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT );
//glUseProgram
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the square data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, vbuf);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// 사각형의 정점에 색상을 설정하려면 다음과 같이 컬러버퍼를 준비하여 핸들과 연결하면 된다
GLES20.glVertexAttribPointer(maColorHandle, 3, GLES20.GL_FLOAT, false, 12, cbuf);
GLES20.glEnableVertexAttribArray(maColorHandle);
Matrix.setIdentityM(mMMatrix, 0);
Matrix.setRotateM(mMMatrix, 0, mAngle++, 0, 0, 1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
// Apply a ModelView Projection transformation
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the square(2개의 삼각형이므로 정점은 6개)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0,0,width,height);
float ratio = (float) width/height;
//this projection matrix is applied to object coodinates
//in the onDrawFrame() method
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);
initShapes();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
}
private int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
private void initShapes(){
float squareCoords[] = {
// X, Y, Z
-0.5f, 0.5f, 0f,
-0.5f, -0.5f, 0f,
0.5f, -0.5f, 0f,
-0.5f, 0.5f, 0f,
0.5f, -0.5f, 0f,
0.5f, 0.5f, 0f
};
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(squareCoords.length * 4);
vbb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order
vbuf = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
vbuf.put(squareCoords); // add the coordinates to the FloatBuffer
vbuf.position(0); // set the buffer to read the first coordinate
float [] colors = {
// R, G, B, A
1f, 0f, 0f, 1f,
0f, 1f, 0f, 1f,
0f, 0f, 1f, 1f,
0.5f, 0.5f, 0f, 1f,
0f, 0.5f, 0.5f, 1f,
0f, 0.5f, 0.5f, 1f,
};
cbuf = ByteBuffer.allocateDirect(colors.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
cbuf.put(colors);
cbuf.position(0);
}
}