>  기사  >  Java  >  Android 개발에서 OpenGL ES가 3D 그래픽을 그리는 예에 대한 자세한 설명

Android 개발에서 OpenGL ES가 3D 그래픽을 그리는 예에 대한 자세한 설명

Y2J
Y2J원래의
2017-04-22 09:50:152213검색

이 글은 주로 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에는 EGLDisplay: 디스플레이 핸들, EGLConfig: 구성 클래스, EGLContext: 렌더링 클래스 및 EGLSurface: 렌더링 가능한 뷰 클래스라는 4개의 클래스가 포함되어 있습니다.

EGL은 OpenGL ES와 기본 윈도우 시스템 사이의 중간 계층으로 생각할 수 있습니다. 로컬 윈도우 시스템은 GNU/Linux나 Mac OX X의 Quartz 등의 X 윈도우 시스템을 말합니다. 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);
//初始化EGLDispla的同时获得版本号
int[] version = new int[2];
egl.eglInitialize(dpy, version);

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

여기서 디스플레이는 유효한 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 프로그래밍 인터페이스를 획득합니다. 다음 프로그램 조각은 위의 내용을 요약합니다.

아아아아

3D 그래픽 제작을 위한 포인트

포인트는 3D 모델을 제작하는 기본입니다. OpenGL ES의 내부 계산은 포인트 기반입니다. 점은 광원의 위치와 물체의 위치를 ​​나타내는 데에도 사용할 수 있습니다. 일반적으로 우리는 점을 표현하기 위해 부동 소수점 숫자 세트를 사용합니다. 예를 들어 정사각형의 꼭지점 4개는 다음과 같이 표현할 수 있습니다.

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();

성능을 향상하려면 부동 소수점 배열을 바이트 버퍼에 저장해야 합니다. 따라서 다음 작업이 있습니다.

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, //右上
};

여기서 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和2n之间共定义了n个线段,总共绘制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+2定义了第n个三角形,总共绘制N-2个三角形。


GL_TRIANGLE_FAN:绘制一组相连的三角形。三角形是由第一个顶点及其后给定的顶点所确定。顶点1,n+1和n+2定义了第n个三角形,总共绘制N-2个三角形。

绘制函数:

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

glDrawArrays创建一个几何图元序列,使用每个被的数组中从first开始,到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();
}

总结:

이 글에서는 아이폰에서 OpenGL ES를 이용하여 그래픽을 그리는 기본 개념과 방법을 소개합니다. OpenGL ES에는 텍스처, 조명 및 재료, 블렌딩, 안개, 마스크, 반사, 3D 모델 로딩 등과 같은 다른 많은 것들이 있습니다. OpenGL ES 기능을 사용하여 풍부한 그래픽 애플리케이션과 게임 인터페이스를 그릴 수 있습니다.

위 내용은 Android 개발에서 OpenGL ES가 3D 그래픽을 그리는 예에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.