English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android 개발 OpenGL ES 그리기3D 그래픽 예제 자세히 설명

OpenGL ES는OpenGL 3차원 그래픽 API의 부集合으로, 휴대폰, PDA, 게임主机 등의嵌入式 장치를 위해 설계되었습니다. Ophone은 현재 OpenGL ES를 지원합니다. 1.0, OpenGL ES 1.0은OpenGL 1.3 규격을 기반으로 합니다. OpenGL ES 1.1 OpenGL 1.5 규격을 기반으로 합니다. 이 문서는 OpenGL ES를 사용하여 그래픽을 그리는 기본 단계를 주로 소개합니다.

이 문서는 세 부분으로 구성되어 있습니다. 먼저 EGL을 통해 OpenGL ES의 프로그래밍 인터페이스를 얻습니다; 그런 다음 구축에 대해 설명합니다.3D 프로그래밍의 기본 개념; 마지막으로는 애플리케이션 예제입니다.

OpenGL ES는본질적으로 그래픽 렌더링 파이프라인의 상태 메커니즘입니다. EGL은 이러한 상태를 모니터링하고 프레임 버퍼 및 다른 렌더링 표면을 유지하는 외부 층입니다.1 이는 표준적인 EGL 시스템 레이아웃 도면입니다. EGL 윈도우 디자인은 사람들이 잘 알고 있는 Microsoft Windows(WGL)과 UNIX(GLX)에서 사용하는 OpenGL의 내장 인터페이스를 기반으로 하며, 후자와 비교하여 더 가깝습니다. OpenGL ES 그래픽 렌더링 파이프라인의 상태는 EGL 관리의 컨텍스트에 저장됩니다. 프레임 버퍼와 다른 렌더링 표면은 EGL API를 통해 생성, 관리 및 파괴됩니다. EGL은 또한 장치 디스플레이와 가능한 장치 렌더링 구성에 대한 접근을 제공하고 제어합니다.


그림1

OpenGL ES는 렌더링 컨텍스트와 렌더링 면이 필요합니다. 렌더링 컨텍스트는 OpenGL ES의 상태 정보를 저장하며, 렌더링 면은 그래픽 원소를 그리는 데 사용됩니다. OpenGL ES를 작성하기 전에 EGL의 작업을 수행해야 하는 것은 다음과 같습니다:

장치가 지원할 수 있는 디스플레이 핸들을 확인하고 초기화합니다.

렌더링 면을 생성하고 OpenGL ES 그래픽을 그립니다.

렌더링 컨텍스트를 생성합니다. EGL은 OpenGL ES 렌더링 컨텍스트를 생성하여 특정 렌더링 면에 연결해야 합니다.

Ophone에서 EGL은 포함됩니다4의 클래스로, EGLDisplay: 디스플레이 핸들, EGLConfig: 구성 클래스; EGLContext: 렌더링 컨텍스트;의 클래스와 EGLSurface: 렌더링 가능한 뷰 클래스로 구성됩니다.

EGL은 OpenGL ES와 로컬 윈도우 시스템 간의 중간 레이어로 볼 수 있습니다. 로컬 윈도우 시스템은 GNU/Linux의 X 윈도우 시스템이나 Mac OS X의 Quartz 등. EGL이 렌더링 면의 유형을 확정하기 전에, EGL은 하위 윈도우 시스템과 통신해야 합니다. 다른 운영 체제에서의 윈도우 시스템의 차이로 인해, EGL은 투명한 윈도우 유형을 제공하며, EGLDisplay입니다. 이는 다양한 윈도우 시스템을 모델링합니다. 따라서 먼저 EGLDisplay 객체를 생성하고 초기화해야 합니다.

// EGLContext의 정적 메서드 getEGL은 EGL 인스턴스를 얻습니다
EGL10 egl = (EGL10)EGLContext.getEGL();
//EGLDisplay를 생성하면, EGL_DEFAULT_DISPLAY는 기본적인 로컬 윈도우 시스템 유형을 얻습니다
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
//EGLDisplay를 초기화하면서 버전 번호를 얻습니다
int[] version = new int[2]);
egl.eglInitialize(dpy, version);

모든 EGLDisplay는 사용 전에 초기화되어야 합니다. EGLDisplay를 초기화하면서 시스템에서 EGL의 구현 버전 번호를 얻을 수 있습니다. 버전 번호를 통해 적절히OpenGL ES API를 사용하여 호환성이 좋은 프로그램을 작성할 수 있으며, 더 많은 장치에 적응하고 최대한의 이동성을 제공할 수 있습니다. 초기화 함수 원형:
boolean eglInitialize(EGLDisplay display, int[] major_minor)

그 중 display는 유효한 EGLDisplay 인스턴스입니다. 함수 호출이 완료되면, major_minor은 현재 EGL 버전 번호를 할당받습니다. 예를 들어 EGL1.0 , major_minor[0]는1,major_minor[1]가 0입니다. EGLSurface는 EGL 렌더링면 관련 모든 정보를 포함하고 있습니다. EGLSurface 구성 정보를 확인하는 방법은 두 가지입니다. 하나는 모든 구성 정보를 확인하여 가장 적합한 것을 선택하는 것이며, 다른 하나는 구성 정보를 지정하여 시스템이 최적의 매칭 결과를 제공하는 것입니다. 일반적으로 두 번째 방법을 사용합니다. 사용자는 configSpec를 통해 원하는 구성을 지정하고, 함수 eglChooseConfig는 매개변수 Configs를 통해 최적의 구성 목록을 반환합니다. 이후 이를 통해 eglCreateContext를 호출하여 렌더링 컨텍스트를 생성하고, 이 함수는 EGLContext 구조를 반환합니다. 렌더링면 EGLSurface의 생성은 eglCreateWindowSurface 함수를 통해 완료됩니다. 애플리케이션은 여러 개의 EGLContext를 생성할 수 있습니다. eglMakeCurrent는 특정 렌더링 컨텍스트를 렌더링면에 바인딩하는 것입니다. eglGetCurrentContext, eglGetCurrentDisplay, eglGetCurrentSurface 함수는 현재 시스템의 렌더링 컨텍스트, 디스플레이 핸들, 렌더링면을 얻는 데 사용됩니다. 마지막으로 EGLContext의 스태틱 메서드 getGL은 OpenGL ES의 프로그래밍 인터페이스를 얻습니다. 아래의 프로그램 스니펫은 이 내용을 요약합니다.

EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2]);
egl.eglInitialize(dpy, version);
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1]);
int[] num_config = new int[1]);
egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
EGLConfig config = configs[0];
EGLContext context = egl.eglCreateContext(dpy, config,
EGL10.EGL_NO_CONTEXT, null);
EGLSurface surface = egl.eglCreateWindowSurface(dpy, config,
sHolder, null);
egl.eglMakeCurrent(dpy, surface, surface, context);
GL10 gl = (GL10)context.getGL();

구성합니다3D 그래픽의 점을

점은3D 모델의 기본입니다. OpenGL ES의 내부 계산은 포인트에 기반합니다. 포인트를 사용하여 빛의 위치, 물체의 위치를 나타낼 수 있습니다. 일반적으로 포인트를 나타내기 위해浮点수 집합을 사용합니다. 예를 들어, 정사각형의4개의 정점이 나타낼 수 있습니다:

float vertices[] = {
-1.0f, 1.0f, 0.0f, //왼쪽 상단
-1.0f, -1.0f, 0.0f, //왼쪽 하단
1.0f, -1.0f, 0.0f, //오른쪽 하단
1.0f, 1.0f, 0.0f, //오른쪽 상단
};

성능을 향상시키기 위해, 플로팅 포인트 배열을 바이트 버퍼에 저장해야 합니다. 따라서 다음과 같은 작업이 있습니다:

ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

ByteOrder.nativeOrder()는 기본 바이트 순서를 얻습니다. OpenGL ES는 그래픽 렌더링 파이프라인을 처리하는 함수를 가지고 있으며, 기본적으로 이 함수의 사용 상태는 꺼져 있습니다. 이 함수를 활성화하고 비활성화할 수 있습니다. glEnableClientState와 glDisableClientState를 사용하여 이를 완료합니다.

// 필요한 정점 배열을 지정합니다
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 배열의 타입과 바이트 버퍼를 설명합니다. 타입은 GL_FLOAT입니다
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// 사용하지 않을 때, 정점 배열을 닫습니다
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);


선은 두 점을 연결하는 일련의 선이며, 다각형 면의 경계입니다.

다각형

다각형은 선으로 구성된 단일 닫힌 루프입니다. OpenGL ES의 다각형은 점진형 다각형이어야 합니다. 다각형 내부에서 어떤 두 점을 선택하더라도 이 두 점을 연결하는 선분이 다각형 내부에 있으면, 이 다각형은 점진형 다각형입니다. 다각형을 그리는 때에는 렌더링 방향을 지정해야 합니다. 그것은 시계 방향과 반시계 방향으로 나뉩니다. 방향은 다각형의 방향을 결정합니다. 즉, 앞면과 뒷면입니다. 가려진 부분을 렌더링하지 않으면 프로그램 성능을 효과적으로 향상시킬 수 있습니다. 함수 glFrontFace는 렌더링 정점의 방향을 정의합니다.

// CCW 방향을 '면'으로 설정합니다. CCW는 CounterClockWise, 반시계 방향
glFrontFace(GL_CCW);
// CW 방향을 '면'으로 설정합니다. CW는 ClockWise, 시계 방향
glFrontFace(GL_CW);

렌더링

이상의 개념 설명을 마친 후, 지금부터 가장 중요한 작업인 렌더링에 들어갑니다. 렌더링은 물체 좌표로 지정된 지오메트리 원소를 프레임 버퍼에 있는 이미지로 변환하는 것입니다. 이미지와 정점 좌표는 밀접한 관계를 가지고 있습니다. 이 관계는 드로잉 모드를 통해 제공됩니다. 자주 사용되는 드로잉 모드는 GL_POINTS, GL_LINE_STRIP,

GL_LINE_LOOP, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN. 이제 각각을 설명합니다:
GL_POINTS: 각 정점을 점으로 처리합니다. 정점 n은 점 n을 정의하며, 총으로 n개의 점을 그립니다.

GL_LINES: 각 정점을 독립적인 선분으로 처리합니다. 정점2n-1와2개의 정점 사이에서 정의된 총으로 N개의 선분을 그립니다./2개의 선분., N이奇수면 마지막 정점을 무시합니다.

GL_LINE_STRIP: 첫 번째 정점부터 마지막 정점까지 연속으로 연결된 선분 그룹을 그립니다. 제 n번째와 n+1개의 정점이 선분 n을 정의하며, 총으로 N-1개의 선분.


GL_LINE_LOOP: 첫 번째 정점부터 마지막 정점까지 연속으로 연결된 선분 그룹을 그립니다. 그리고 마지막 정점과 첫 번째 정점을 연결합니다. 제 n번째와 n+1개의 정점이 선분 n을 정의하며, 마지막 선분은 정점 N과1사이에서 정의된 총으로 N개의 선분을 그립니다.


GL_TRIANGLES: 각 세 개의 정점을 독립적인 삼각형으로 처리합니다. 정점3n-2,3n-1와3n정의된 n번째 삼각형, 총으로 N/3개의 삼각형.

GL_TRIANGLE_STRIP: 연결된 삼각형 그룹을 그립니다.奇수 점 n에 대해, 정점 n, n+1와 n+2정의된 n번째 삼각형; 짝수 n에 대해, 정점 n+1,n과 n+2n번째 삼각형을 정의하며, 총으로 N-2개의 삼각형.


GL_TRIANGLE_FAN: 연결된 삼각형 그룹을 그립니다. 삼각형은 첫 번째 정점과 이후로 주어진 정점으로 정의됩니다. 정점1,n+1와 n+2n번째 삼각형을 정의하며, 총으로 N-2개의 삼각형.

드로잉 함수:

void glDrawArrays(int mode, int first, int count)
void glDrawElements(int mode, int count, int type, Buffer indices)

glDrawArrays는 지오메트리 원소 시퀀스를 생성하며, 각 배열에서 first부터 시작하여 + count – 1마지막 배열 요소, mode는 그리기 모드입니다.

glDrawElements는 count 개의 요소를 사용하여 그리기 모드를 정의합니다. type는 indices 배열의 데이터 타입입니다. mode는 그리기 모드입니다. indices 배열은 버텍스를 저장합니다

포인트의 인덱스 값.

응용 예제

위에서 설명한 내용을 사용하여 Ophone에서 구형을 그리는3D 구형 프로그램입니다. 효과 그림如下:


그림2 구형 예제

주요 그리기 프로그램:

static private FloatBuffer vertex;//버텍스에 대한 바이트 버퍼
static private FloatBuffer normal;//법선 벡터에 대한 바이트 버퍼
float[] lightPos = new float[] {10.0f, 10.0f, 10.0f, 1.0f };//빛의 좌표
private static final int STEP = 24;//
private static final float RADIUS = 1.0f;//반지름
protected void init(GL10 gl) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//배경 색상 설정
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);
gl.glEnable(GL10.GL_LIGHTING);//빛 사용
gl.glEnable(GL10.GL_LIGHT0); //빛을 켭니다
gl.glClearDepthf(1.0f);//깊이 캐시 설정
gl.glDepthFunc(GL10.GL_LEQUAL);//깊이 캐시 비교 함수 설정, GL_LEQUAL은 새로운 픽셀의 깊이 캐시 값이 현재 픽셀의 깊이 캐시 값보다 작거나 같을 때 깊이 테스트를 통과합니다
gl.glEnable(GL10.GL_DEPTH_TEST);//깊이 캐시 사용
gl.glEnable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);//설정 그림자 모드 GL_SMOOTH
}
protected void drawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, 7f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//
drawSphere(gl, RADIUS, STEP, STEP); //绘制球形
}
public static void gluLookAt (GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)

它共接受三组坐标,分别为eye、 center和up。eye表示我们眼睛在"世界坐标系"中的位置,center表示眼睛"看"的那个点的坐标,up坐标表示观察者本身的方向,如果将观察点比喻成我们的眼睛,那么这个up则表示我们是正立还是倒立异或某一个角度在看,这里是正立方式,所以是{0,1,0}。

private static void drawSphere(GL10 gl, float radius,
int stacks, int slices) {
vertex=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );
normal=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );
int i, j, triangles;
float slicestep, stackstep;
stackstep = ((float)Math.PI) / stacks;
slicestep = 2.0f * ((float)Math.PI) / slices;
for (i = 0; i < stacks; ++i)
{
float a = i * stackstep;
float b = a + stackstep;
float s0 = (float)Math.sin(a);
float s1 = (float)Math.sin(b);
float c0 = (float)Math.cos(a);
float c1 = (float)Math.cos(b);
float nv;
for (j = 0; j <= slices; ++j)
{
float c = j * slicestep;
float x = (float)Math.cos(c);
float y = (float)Math.sin(c);
nv=x * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=c0;
normal.put(nv);
vertex.put( nv * radius);
nv=x * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=c1;
normal.put(nv);
vertex.put( nv * radius);
}
}
normal.position(0);
vertex.position(0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
gl.glNormalPointer(GL10.GL_FLOAT, 0, normal);
gl.glEnableClientState (GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState (GL10.GL_NORMAL_ARRAY);
triangles = (slices + 1) * 2;
for(i = 0; i < stacks; i++)
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,
i * triangles, triangles);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
private static FloatBuffer allocateFloatBuffer(int capacity){
ByteBuffer vbb = ByteBuffer.allocateDirect(capacity);
vbb.order(ByteOrder.nativeOrder());
return vbb.asFloatBuffer();
}

결론:

이 문서는 Ophone에서 OpenGL ES를 사용하여 그래픽을 그리는 기본 개념과 방법을 소개합니다. OpenGL ES에는 텍스처, 조명 및 재질, 블렌딩, 안개, 마스크, 반사 등 다른 많은 내용이 있습니다.3D 모델의 로드 등. OpenGL ES 함수를 사용하여 다양한 그래픽 애플리케이션과 게임 인터페이스를 그릴 수 있습니다.

좋아하는 것