ホームページ >Java >&#&チュートリアル >Android開発におけるOpenGL ESの3Dグラフィック描画例を詳しく解説
この記事では、3D グラフィックス サンプルを描画するための OpenGL ES の Android 開発に関する関連情報を主に紹介します。
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 を使用してグラフィックスを描画する基本的な手順を主に紹介します。
この記事は 3 つの部分で構成されています。まず、OpenGL ES のプログラミング インターフェイスを EGL を通じて取得し、次に 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 上の X ウィンドウ システムや Mac OX 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); //初始化EGLDispla的同时获得版本号 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 構成情報を照会するには 2 つの方法があります。1 つはすべての構成情報を照会し、最も適切なものを選択する方法で、もう 1 つは構成情報を指定してシステムが最適な結果を提供する方法です。一般的には 2 番目の方法が使用されます。ユーザーは configSpec を通じて希望の構成を指定し、関数 eglChooseConfig はパラメーター Configs を通じて最適な構成リストを返します。次に、取得した Config を使用して 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和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(); }
总结:
この記事では、Ophone で OpenGL ES を使用してグラフィックスを描画する基本概念と方法を紹介します。 OpenGL ES には、テクスチャ、ライティングとマテリアル、ブレンディング、フォグ、マスク、反射、3D モデルの読み込みなど、他にも多くのものがあります。 OpenGL ES 関数を使用して、リッチなグラフィック アプリケーションやゲーム インターフェイスを描画できます。
以上がAndroid開発におけるOpenGL ESの3Dグラフィック描画例を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。