Android OpenGL ES 3.0 Square 사각형 그리기(java 버전)
Android에서 OpenGL ES 3.0 버전을 이용하여 사각형을 그리는 예제입니다.
Google developer 사이트에서 OPenGL을 이용하여 Android에서 삼각형과 사각형 도형을 그리기 위한 설명을 하는 페이지를 간단히 요약하여 소개하고 제공하는 소스를 이용하여 도형을 제작하는 방법까지 바로 이전 글에서 삼각형 관련된 예제로 알아보았습니다.
Android OpenGL ES 3.0 삼각형 그리기(java 버전)
그래서 자세한 설명은 이전 글을 참고하시기 바라며 이번에는 동일한 소스를 사용하여 사각형을 그리는 부분만 추가하여 테스트를 진행합니다.
삼각형을 그릴때는 Triangle.java 파일 만들었는데 사각형을 그리기 위해서 Square.java 파일을 생성하는데 일부 변경이 되고 Renderer 파일도 조금 수정해주면 됩니다.
다른 소스는 전에 생성한 내용을 그대로 사용하고 여기서는 Square와 Renderer 파일의 변경된 부분만 설명합니다.
실행했을 때 결과화면은 아래와 같이 멋없는 사각형입니다.
Square.java 파일을 생성하고 사각형 용으로 도형의 좌표를 표기합니다.
static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f }; // top right |
그림에 있는 좌표를 표시하고 그리는 순서를 정하여 꼭지점의 순서를 정하게 됩니다.
특이한점은 사각형을 사각형으로 그리지 않고 3각형 2개를 그리는 방법으로 진행합니다.
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
이렇게 시계 반대방향으로 순서를 나열하여 좌측과 우측의 삼각형을 그리도록 정의합니다.
// initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); |
이런 작업 순서는 ByteBuffer에 저장해 둡니다.
나머지 소스들은 삼각형일때 triangleCoords로 되어있던 부분을 squareCoords로 변경합니다.
draw() 함수에서 삼각형은 꼭지점을 이용하여 그리고 사각형은 Buffer에 있는 순서를 이용하여 그리도록 수정됩니다.
// Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Draw the square GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); |
MyGLRenderer.java 파일에서는 MVP Matrix가 사용됩니다.
MVP(Model, View, Projection)은 카메라에서 모델을 바라보는 형상을 표시하는 것인데
삼각형에서는 사용하지 않았는데 사각형에서는 왜 필요한지 지식이 짧아 좀더 학습이 필요하네요.
@Override public void onDrawFrame(GL10 gl) { float[] scratch = new float[16]; // Draw background color GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT); // Set the camera position (View matrix) Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Calculate the projection and view transformation Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); // Draw square mSquare.draw(mMVPMatrix); } |
그리는 부분에서 삼각형은 Array, 사각형은 Element의 차이인데 나중에 알게되면 내용 추가를 해야 할 것 같습니다.
수정되는 부분이 많지 않아 설명이 짧게 끝났습니다.
아래는 전체 소스입니다.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.OpenGLTriangle"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package com.copycoding.opengltriangle;
import androidx.appcompat.app.AppCompatActivity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private GLSurfaceView gLView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
// Create a GLSurfaceView instance and set it
// as the ContentView for this Activity.
gLView = new MyGLSurfaceView(this);
setContentView(gLView);
}
}
MyGLRenderer.java
package com.copycoding.openglsquare;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyGLRenderer implements GLSurfaceView.Renderer {
// private Triangle mTriangle;
private Square mSquare;
private static final String TAG = "MyGLRenderer";
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private float mAngle;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Set the background frame color
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// initialize a triangle
mSquare = new Square();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
@Override
public void onDrawFrame(GL10 gl) {
float[] scratch = new float[16];
// Draw background color
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Draw square
mSquare.draw(mMVPMatrix);
}
public static 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 = GLES30.glCreateShader(type);
// add the source code to the shader and compile it
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
}
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
}
MyGLSurfaceView.java
package com.copycoding.opengltriangle;
import android.content.Context;
import android.opengl.GLSurfaceView;
public class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer renderer;
public MyGLSurfaceView(Context context) {
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
renderer = new MyGLRenderer();
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(renderer);
}
}
Square.java
package com.copycoding.openglsquare;
import android.opengl.GLES30;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
public class Square {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// The matrix must be included as a modifier of gl_Position.
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f // top right
};
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
private final int mProgram;
private int positionHandle;
private int colorHandle;
private int mVPMatrixHandle;
// private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
squareCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(squareCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES30.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES30.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// create empty OpenGL ES Program
mProgram = GLES30.glCreateProgram();
// add the vertex shader to program
GLES30.glAttachShader(mProgram, vertexShader);
// add the fragment shader to program
GLES30.glAttachShader(mProgram, fragmentShader);
// creates OpenGL ES program executables
GLES30.glLinkProgram(mProgram);
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL ES environment
GLES30.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
positionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES30.glEnableVertexAttribArray(positionHandle);
// Prepare the triangle coordinate data
GLES30.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
GLES30.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
colorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES30.glUniform4fv(colorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES30.glUniformMatrix4fv(mVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES30.glDrawElements(
GLES30.GL_TRIANGLES, drawOrder.length,
GLES30.GL_UNSIGNED_SHORT, drawListBuffer);
// Draw the triangle
// GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES30.glDisableVertexAttribArray(positionHandle);
}
}
소스 바탕색이 이상하게 변하네요...
- copy coding -