以上の処理により準備が整いましたので、いよいよ立方体の描画とアニメーションの処理を行います。基本的に3Dのアニメーションは毎フレームごとに頂点座標を移動させる計算により、立体物が動いているように見せるというものです。
void drawCube(oes3d* pMe){
uint32 now_time;
now_time = ISHELL_GetUpTimeMS (pMe->a.m_pIShell); //最新時間の取得
if( (now_time - pMe->OldTime) > 40 ) {
pMe->OldTime = now_time; //時間更新
// クリア処理
IGL_glClear (pMe->m_pIGL, GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT); // バッファクリア
IGL_glLoadIdentity (pMe->m_pIGL); // 累積をクリア
// 立方体の移動
IGL_glTranslatex(pMe->m_pIGL, f16(0),
f16(0), -f16(45)); // 立方体を表示位置まで移動
// 立方体の回転処理
pMe->m_Angle = f16( pMe->m_Counter++ ); // 回転角度
IGL_glRotatex (pMe->m_pIGL, pMe->m_Angle,
f16(1), f16(0), f16(0)); // 縦回転
IGL_glRotatex (pMe->m_pIGL, pMe->m_Angle,
f16(0), f16(1), f16(0)); // 横回転
// 配列データから立方体を描画
IGL_glNormalPointer (pMe->m_pIGL, GL_BYTE, 0,
pMe->m_Normals); // 法線配列を設定
IGL_glVertexPointer (pMe->m_pIGL, 3, GL_BYTE, 0,
pMe->m_Vertices ); // 立方体の座標を設定
IGL_glDrawElements (pMe->m_pIGL, GL_TRIANGLES, 6*6,
GL_UNSIGNED_BYTE, pMe->m_Indices);
// 画面更新
IEGL_eglSwapBuffers (pMe->m_pIEGL, pMe->m_eglDisplay,
pMe->m_eglSurface );
if ( IGL_glGetError (pMe->m_pIGL) != GL_NO_ERROR ){
cleanup (pMe);
}
}
CALLBACK_Init (&pMe->Callback, (PFNNOTIFY)drawCube, pMe);
ISHELL_Resume (pMe->a.m_pIShell, &pMe->Callback);
}
| ソース3 ※レイアウトの都合上一部改行している個所があります。 |
関数宣言とApplet構造体に以下の値も設定し、「EVT_APP_START」などのイベントから「setup()」関数を呼ぶようにします。
void drawCube(oes3d* pMe);
AEECallback Callback; //コールバックの状態維持 uint32 OldTime; //更新時間の格納 GLfixed m_Angle; //3D を回転させる角度 GLfixed m_Counter; //3D を回転させる角度のパラメータ
case EVT_APP_START:
if(setup(pMe) == TRUE){
drawCube(pMe);
}else{
cleanup(pMe);
}
return(TRUE);
最初に「IGL_glClear()」と「IGL_glLoadIdentity()」によるクリア処理を行っていますが、これはOpenGL ESが前回の処理結果をバッファに残しているので、必要な処理になります。
ここでのポイントは“立方体の移動と回転”です。ここで3Dグラフィックスの前提の話に戻りますが、基本的に3Dオブジェクトの実体は頂点座標です。つまり、3Dオブジェクトの動作は頂点座標の移動(回転なども含む)であり、この移動は行列計算(アフィン変換)によって実現されます。無論、この計算は汎用的なものなので関数化されており、計算はOpenGL ES側で行われます。例えば、「IGL_glLoadIdentity()」は累積をクリアするために単位行列をかけるもので、この計算が実装された関数ということになります。
話は戻りますが、立方体を移動させる場合に利用するのが「IGL_glTranslatex()」で、頂点座標の平行移動を計算してくれます。ここでは(0,0,-45)の座標に立方体を移動させています。これには2つ理由があります。1つが立方体のデータを設定する際に、適当な座標値を用いて立方体を定義していますので、描画可能な視錐台の適当な場所に立方体を動かす必要があるためです。ループの中で毎回これを実行しているのは計算の複雑さを回避するために、原点(0,0,0)を中心として行っているためです。もう1つがマイナスz方向への移動ですが、これはOpenGL ESのカメラがマイナスz方向に向いているためです。
次に「IGL_glRotatex()」による回転です。ここでは横・縦の方向を座標で指定し、角度を毎フレーム変更することにより回転を行っています。1つ重要な点は、移動を行った後に回転をすることです。これが逆になると立方体が想定外の方向に移動してしまいます。
最後に設定した頂点、法線をレンダリングしますが、ここでは頂点インデックスで立方体を設定していますので、「IGL_glDrawElements()」によるレンダリングを行います。第3引数に指定してある、6×6(36)は頂点インデックスの配列の要素数で、この数だけ頂点インデックスがあることを示しています。
後は、2Dアニメーションの際にも実装したBREWでのコールバックを実装して、繰り返し処理を実現させることにより、3Dによるアニメーションが行われます。
以上、3D立方体の描画を見てきましたが、単純な立方体を描画して、少し動かすだけでも結構大変だということがお分かりいただけたかと思います。OpenGL ESは、プリミティブな3Dグラフィックスの機能しか提供していません。基本機能であるため、柔軟にプログラマの意図したことが実装できる半面、3Dグラフィックスに精通していないと扱いが大変で、複雑なことをしようとした場合、ライブラリを自身で実装するなどの手間が掛かります。このため、ゲームなどの複雑な3Dグラフィックスを実現するためには自社で専用ライブラリを実装するか、高レベルのミドルウェアを適用するなどして実装インパクトを少なくします。
そこで次回は、BREW環境でスタンダードな3Dのミドルウェア(高レベルAPIセット)として利用されている「MascotCapsule」を用い、より簡易で高度な3Dグラフィックスの利用について解説したいと思います。ご期待ください!(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.
組み込み開発の記事ランキング
コーナーリンク