오늘은 OpenGLES 로 삼각형을 그려보도록 하겠습니다.
[결과물]
[프로젝트 파일]
[AndroidMenifest.xml]
...
</application>
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
</manifest>
[MainActivity.java]
1. 뷰를 생성하고, 셋팅해준다.
package com.samman.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends Activity {
private MainGLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAGS_CHANGED, WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGLSurfaceView = new MainGLSurfaceView(this);
//렌더링 된것이 나타날 곳을 GLSurfaceView 으로 지정한다.
setContentView(mGLSurfaceView);
}
}
[MainGLSurfaceView.java]
MainActivity 에서 지정해줄 GLSurfaceView클래스를 만든다.
package com.samman.myapplication;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
public class MainGLSurfaceView extends GLSurfaceView {
private final MainGLRenderer mGLRenderer;
public MainGLSurfaceView(Context context) {
super(context);
//OpenGLES 2.0을 사용하기 위해 EGL 컨텍스트를 설정하는 메서드입니다.
//EGL (Embedded System Graphics Library)은 안드로이드에서 OpenGL과 같은 그래픽 라이브러리를 사용할 수 있도록 하는 인터페이스입니다.
setEGLContextClientVersion(2);
//Renderer를 셋팅합니다.
mGLRenderer = new MainGLRenderer();
setRenderer(mGLRenderer);
//GLRenderer에서 구현된 onDrawFrame 이라는 함수를 매프레임마다 계속 호출하게 된다.
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
[MainGLRenderer.java]
GLSurfaceView 에서 그리기를 담당해줄 Renderer 를 만든다.
내부에는 onDrawFrame이라는 프레임을 그리는 메소드가 있다.
package com.samman.myapplication;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MainGLRenderer implements GLSurfaceView.Renderer {
Triangle mTriangle;
//보통 on 을 쓰면 event 를 받는 함수를 나타내죠.
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//필요한 OpenGLES 의 객체를 생성한다.
//삼각형을 그리는 메소드가 포함된 Triangle 클래스를 초기화한다.
mTriangle = new Triangle();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
// GLSurfaceView의 크기가 변경될 때 호출됩니다. 이 메서드에서는 변경된 뷰 크기에 맞게 OpenGL ES 뷰포트를 업데이트합니다.
GLES20.glViewport(0,0,width, height);
}
@Override
//OpenGLES에서 그래픽랜더링 단계중 '프레임 렌더링'을 수행하는 콜백 메소드이다 ( GLSurfaceView.Renderer인터페이스에 포함 ).
//View 에 포함되는 모든 객체를 그리고, Frame 완료를 알리는 신호를 보낸다.
//프레임이란 : FPS 는 1초에 몇번의 뷰를 그릴것인가에 대한 Frame 수를 나타내는 것
//프레임 렌더링이란 : 한 번의 프레임 렌더링이 수행되고, 이것이 뷰의 화면에 업데이트 되기 위한 한 단계일 뿐입니다.
public void onDrawFrame(GL10 gl10) {
//바탕색 셋팅
GLES20.glClearColor(0.3f,0.3f,0.0f,0.3f); //GLES20은 정적 클래스이고, OpenGL ES 2.0에서 사용할 수 있는 모든 함수를 제공함
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //뷰의 화면을 초기화 하기전에 glClear 를 통해 버퍼를 지운다
//삼각형 그리기
mTriangle.draw();// ( VertexShader,FragmentShader 컴파일 -> Program에 셋팅 및 링크 -> 쉐이더를 통해 얻은 Vertex,Color Handler로 값 셋팅 -> 그리기 )
//OpenGL ES에서는 명령을 큐에 저장하고, 필요할 때마다 실행합니다.
//그러나 glFlush() 메서드를 호출하면, 현재까지의 그래픽렌더링 명령을 모두 실행하도록 강제합니다.
GLES20.glFlush(); // 그리라고 하는 명령어이다.
}
}
[ Triangle.java ]
- 삼각형을 그리기 위한 좌표값 배열과 색상 값
- 좌표들과 색상값을 셋팅하기 위한 VertexShader, FragmentShader 셋팅
- 쉐이더끼리 링킹하고 결과 값을 얻기위한 Program
- Program에 엮여있는 Shader 를 통해 얻은 값을 사용하기 위한 Handler
package com.samman.myapplication;
import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class Triangle {
//삼각형 정점 3개의 좌표
static float triangleCoords[] = {
0.0f, 0.5f, 0.0f, //top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f //bottom right
};
private float color[] = {0.5f, 0.5f,0.5f, 0.5f};
//Vertex Shader ( Shader: 정점에 처리할 연산을 정의하는 메소드 )
private final String mVertexShaderCode = // Vertex로 포현할 position 프로그램 코드
"attribute vec4 vPosition;"+ // 객체의 위치를 저장하기 위한 vec4 자료형(4개의 int로 이루어진 벡터)
"void main(){"+ // 셰이더함수
"gl_Position = vPosition;"+ // 현재 vertex의 좌표 값을 저장함
"}";
//Fragment Shader ( 픽셀에 색상을 넣음 )
private final String mFragmenetShaderCode =
"precision mediump float;\n" +
"uniform vec4 vColor;\n" +
"void main() {\n" +
" gl_FragColor = vColor;\n" +
"}\n";
//정점을 이루는 좌표의 갯수
static final int COORDS_PER_VERTEX = 3;
//정점(꼭지점) 갯수
private final int vertextCount = triangleCoords.length /COORDS_PER_VERTEX;
//정점을 이루는 크기(Bytes)( 정점의 좌표값들은 보통 Float 으로 표현됨 )
private final int vertexStride = COORDS_PER_VERTEX * 4;
private final int mProgram;
private FloatBuffer mVertexBuffer;
private int mPositionHandle;
private int mColorHandle;
public Triangle() {
//버퍼 셋팅
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4); //바이트버퍼 할당: 정점 3개 * 각 정점의 좌표 3개 * 4Bytes
bb.order(ByteOrder.nativeOrder()); //리틀엔디언,빅엔디언 셋팅
mVertexBuffer = bb.asFloatBuffer(); //FloatBuffer로 변환
mVertexBuffer.put(triangleCoords); //Float으로 이루어진 삼각형좌표 배열을 삽입
mVertexBuffer.position(0); //첫번째 좌표부터 읽도록 positioning
//Shader 셋팅
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, mVertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, mFragmenetShaderCode);
//프로그램 생성 -> 쉐이더 붙이기 -> 프로그램 링크 ( 쉐이더간의 인터페이스를 생성해주고, 최종 결과물까지 나오도록 내부 파이프라인을 연결해준다. )
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram); // 링크된 프로그램은 GLES20.glUseProgram()을 이용해서 사용된다. (아래참고)
}
public void draw(){
GLES20.glUseProgram(mProgram); // VertexShader, FragmentShader가 셋팅되고 링크된 프로그램을 사용한다.
//쉐이더내에서 Handler 로 좌표값 셋팅//
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");// VertexShader를 통해 셋팅된 정점을 읽어들일수 있는 positionHandle을 가져옴
GLES20.glEnableVertexAttribArray(mPositionHandle); //좌표들을 쓸수 있도록 핸들을 기동시킴
GLES20.glVertexAttribPointer( //움직이는 PositionHandle이 float형의 VertexBuffer를 타고다니면서 좌표를 얻어냄
mPositionHandle,
COORDS_PER_VERTEX,
GLES20.GL_FLOAT,
false,
vertexStride,
mVertexBuffer);
//쉐이더내에서 Handler 로 색상 셋팅//
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Fragment Shader 를 통해 셋팅된 컬러 값을 읽어들일수 있는 positionHandle을 가져옴
GLES20.glUniform4fv(mColorHandle, 1, color, 0 ); // 색상을 설정함
//그리기 및 종료
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertextCount); // 그리기
GLES20.glDisableVertexAttribArray(mPositionHandle); //PositionHandle 중지함
}
//쉐이더들을 처리할(컴파일)할 메소드
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
'ComputerGraphics [고려대학교_한정현]' 카테고리의 다른 글
07강 - 래스터라이져 (0) | 2023.04.18 |
---|---|
6강 OpenGLES 1 (0) | 2023.04.11 |
Android + OpenGLES로 사각형 그리기 (0) | 2023.04.02 |
5강(1) - 랜더링 과정 중에서 View Transform ( World Space -> Camera Space 변환) (0) | 2023.03.26 |
4강(4) 3차원에서 World Transform ( Object Space -> World Space 변환 ) (0) | 2023.03.26 |