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

Android開発でOpenGL ESを描画する3D グラフィックインスタンスの詳細

OpenGL ESは、OpenGL 3Dグラフィック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の設定情報を確認する方法は2種類あります。一つはすべての設定情報を確認し、最も適切なものを選ぶことです;もう一つは設定情報を指定し、システムが最適なマッチング結果を提供することです。一般的には後者を使用します。ユーザーはconfigSpecを通じて希望する設定を指定し、eglChooseConfig関数はパラメータConfigsを通じて最適な設定リストを返します。その後、取得した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,逆时针
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本を描画します。2n-1および2GL_LINES:各頂点を独立した線として描画します。頂点/2n間で合計n本の線を定義し、合計N本を描画します。

本の線。Nが奇数の場合、最後の頂点を無視します。+1GL_LINE_STRIP:最初の頂点から最後の頂点まで順に連結された線のグループを描画し、第nおよびn-1本の線。個の頂点が線nを定義し、合計N本を描画します。


GL_LINE_LOOP:最初の頂点から最後の頂点まで順に連結された線のグループを描画し、最後の頂点と最初の頂点を結びます。第nおよびn+1個の頂点が線nを定義し、最後の線は頂点Nおよび1間で定義され、合計N本の線を描画します。


GL_TRIANGLES:各3つの頂点を独立した三角形として描画します。頂点3n-2、3n-1および3nが第n個三角形を定義し、合計N個を描画します。/3三角形。

GL_TRIANGLE_STRIP:連結された三角形のグループを描画します。奇数の頂点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から始まる各配列の要素を使用して、ジオメトリエンティティのシーケンスを作成します。 + 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)

それが3つの座標を引き受けます。それぞれが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関数を使用して、豊かなグラフィックアプリケーションやゲームインターフェースを描画できます。

おすすめ