Android에서 OpenGL ES 3.0 버전을 이용하여 사각형을 그리는 예제입니다.

Google developer 사이트에서 OPenGL을 이용하여 Android에서 삼각형과 사각형 도형을 그리기 위한 설명을 하는 페이지를 간단히 요약하여 소개하고 제공하는 소스를 이용하여 도형을 제작하는 방법까지 바로 이전 글에서 삼각형 관련된 예제로 알아보았습니다.

 


Android OpenGL ES 3.0
삼각형 그리기(java 버전)



 

그래서 자세한 설명은 이전 글을 참고하시기 바라며 이번에는 동일한 소스를 사용하여 사각형을 그리는 부분만 추가하여 테스트를 진행합니다.

삼각형을 그릴때는 Triangle.java 파일 만들었는데 사각형을 그리기 위해서 Square.java 파일을 생성하는데 일부 변경이 되고 Renderer 파일도 조금 수정해주면 됩니다.

 

다른 소스는 전에 생성한 내용을 그대로 사용하고 여기서는 SquareRenderer 파일의 변경된 부분만 설명합니다.

 

실행했을 때 결과화면은 아래와 같이 멋없는 사각형입니다.

 

 

 

 

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 -


+ Recent posts