第3回 OpenGL ESを用いた簡単な図形の描画 実践編( 前半 )
エントリポイント
まずはアプリケーションのエントリポイントを用意します。図2の内容を考慮しつつ、リスト1のように空の関数( または宣言のみ )を用意しておきます。まだ定義していない型や定数などはありますが、後に使いますので、今は図2の内容と大体の対応が取れていることを確認しましょう。
// プラットフォーム共通化のために、それらしい型を定義しておく.
// 64ビット環境で、DisplayやWindowがint32_tの場合などが考えられるので、環境に合わせて適宜変更する.
typedef void* AppNativeDisplay;
typedef void* AppNativeWindow;
// プラットフォーム依存部.
static bool InitializeNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
static void PollNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
static void TerminateNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
// EGL関連.
static bool InitializeEGL(
AppNativeDisplay* pNativeDisplay,
AppNativeWindow* pNativeWindow,
EGLDisplay& display, EGLContext& context, EGLSurface& surface );
static void TerminateEGL( EGLDisplay& display, EGLContext& context, EGLSurface& surface );
// シェーダ関連.
static GLuint CompileShader( GLenum eShaderType, const char* szSrc );
static bool InitializeShaders( SShader* pShader );
static void TerminateShaders( SShader* pShader );
// 描画ループ関連.
static void Draw( SShader* pShader, EGLDisplay& display, EGLSurface& surface );
static void DrawTriangle( SShader* pShader );
int main( int argc, char *argv[] )
{
AppNativeDisplay display = nullptr;
AppNativeWindow window = nullptr;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLContext eglContext = EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL_NO_SURFACE;
SShader sShader;
if( InitializeNativeSystem( &display, &window ) ) {
if( InitializeEGL( &display, &window, eglDisplay, eglContext, eglSurface ) ) {
if( InitializeShaders( &sShader ) ) {
GLint nFrameCount = 0;
while( nFrameCount < NUM_FRAMES ) {
PollNativeSystem( &display, &window );
Draw( &sShader, eglDisplay, eglSurface );
++nFrameCount;
}
TerminateShaders( &sShader );
}
TerminateEGL( eglDisplay, eglContext, eglSurface );
}
TerminateNativeSystem( &display, &window );
}
return 0;
}
プラットフォーム依存部の処理
プラットフォームに依存する部分の初期化と終了処理を実装します。ここでは主にウィンドウシステムの初期化などを行うことになります。何らかの初期化を行った結果を次のEGLの初期化の処理で使うことになるのですが、こちらはプラットフォームに依存しますので、それらしい空関数を入れておきます( リスト2 )。実装はお使いの環境に合わせて記述する流れとなります。
// -------- ここからリスト1で宣言/定義済み --------
// プラットフォーム共通化のために、それらしい型を定義しておく.
// 64ビット環境で、DisplayやWindowがint32_tの場合などが考えられるので、環境に合わせて適宜変更する.
typedef void* AppNativeDisplay;
typedef void* AppNativeWindow;
static bool InitializeNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
static void PollNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
static void TerminateNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow );
// -------- ここまでリスト1で宣言/定義済み --------
// -------- ここからプラットフォーム依存部 --------
static bool InitializeNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow )
{
bool bRet = false;
// -------- 何らかのプラットフォーム依存処理を実行する --------
// 成功したらbRetをtrueに.
// pNativeDisplayとpNativeWindowにはここで初期化したDisplayやWindowを格納.
return bRet;
}
static void PollNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow )
{
if( pNativeDisplay && pNativeWindow ) {
// -------- 何らかのプラットフォーム依存処理を実行する --------
// 定例処理が必要な場合はここに実装する.
}
}
static void TerminateNativeSystem( AppNativeDisplay* pNativeDisplay, AppNativeWindow* pNativeWindow )
{
if( pNativeDisplay && pNativeWindow ) {
// -------- 何らかのプラットフォーム依存処理を実行する --------
// WindowやDisplayの破棄の処理を実装する.
}
}
// -------- ここまでプラットフォーム依存部 --------
EGLの初期化と終了処理
EGLの初期化と終了処理を実装します。いくつか関数を呼び出すだけではあるのですが、上記のプラットフォーム依存部で初期化されたAppNativeWindowとAppNativeDisplay型の変数を使います。
EGLの初期化部分では、まずは、引数で渡されたAppNativeDisplay型の変数からEGLDisplayを取得してEGLのシステムを初期化します。次に、これから作成したい描画面やEGLContextに関するオプションを記述し、EGLConfigを取得します。このEGLConfigを使い、描画面( EGLSurface )の作成、EGLContextの作成をそれぞれ行います。描画面の作成時には、AppNativeWindow型の変数も渡すことになります。
終了処理では、EGLContextの破棄、EGLSurfaceの破棄、そして、EGLのシステムの終了処理をそれぞれ行うAPIをコールする流れとなります。リスト3は上記の処理を記述したものになりますが、EGLがプラットフォーム依存部分と連携していることが感じられると思います。
// -------- ここからリスト1で宣言/定義済み --------
// プラットフォーム共通化のために、それらしい型を定義しておく.
// 64ビット環境で、DisplayやWindowがint32_tの場合などが考えられるので、環境に合わせて適宜変更する.
typedef void* AppNativeDisplay;
typedef void* AppNativeWindow;
static bool InitializeEGL(
AppNativeDisplay* pNativeDisplay,
AppNativeWindow* pNativeWindow,
EGLDisplay& display, EGLContext& context, EGLSurface& surface );
static void TerminateEGL( EGLDisplay& display, EGLContext& context, EGLSurface& surface );
// -------- ここまでリスト1で宣言/定義済み --------
// -------- ここからEGLの初期化と終了処理 --------
static bool InitializeEGL(
AppNativeDisplay* pNativeDisplay,
AppNativeWindow* pNativeWindow,
EGLDisplay& display, EGLContext& context, EGLSurface& surface )
{
bool bRet = false;
// 描画面に関する設定.この設定が使えるかどうかをChooseConfigする.
EGLint aConfigAttrib[] = {
EGL_BUFFER_SIZE, 16,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
// EGLContextを作るときに渡すオプション.OpenGL ES 2.0の場合は下記で固定.
EGLint aCtxAttrib[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLConfig config = nullptr;
EGLint nNumConfig = 0;
if( pNativeDisplay && pNativeWindow ) {
// AppNativeDisplayからEGLDisplayを取得する.
display = eglGetDisplay( reinterpret_cast(*pNativeDisplay) );
if( EGL_NO_DISPLAY == display ) {
std::cerr << "Error eglGetDisplay." << std::endl;
return bRet;
}
// 取得したEGLDisplayでEGLのシステムを初期化.
if( !eglInitialize( display, nullptr, nullptr ) ) {
std::cerr << "Error eglInitialize." << std::endl;
return bRet;
}
// OpenGL ESを使用すると宣言する.
eglBindAPI( EGL_OPENGL_ES_API );
// 上記で設定した描画面をサポートするEGLConfigがこのドライバにあるかどうかをチェック.
if( !eglChooseConfig( display, aConfigAttrib, &config, 1, &nNumConfig ) ) {
std::cerr << "Error eglChooseConfig." << std::endl;
return bRet;
}
if( nNumConfig != 1 ) {
std::cerr << "Error nNumConfig." << std::endl;
return bRet;
}
// 上記EGLConfigが存在する場合は描画面を生成.
// このときにAppNativeWindowを渡す.
surface = eglCreateWindowSurface(
display,
config,
reinterpret_cast( *pNativeWindow ),
nullptr
);
if( EGL_NO_SURFACE == surface ) {
std::cerr << "Error eglCreateWindowSurface. " << eglGetError() << std::endl;
return bRet;
}
// 同様に上記で得られたEGLConfigを渡してEGLContextを作成する.
context = eglCreateContext( display, config, EGL_NO_CONTEXT, aCtxAttrib );
if( EGL_NO_CONTEXT == context ) {
std::cerr << "Error eglCreateContext. " << eglGetError() << std::endl;
return bRet;
}
// 最後に、このEGLSurfaceとEGLContextをアクティブなものにする..
eglMakeCurrent( display, surface, surface, context );
bRet = true;
}
return bRet;
}
static void TerminateEGL( EGLDisplay &display, EGLContext &context, EGLSurface &surface )
{
eglDestroyContext( display, context );
eglDestroySurface( display, surface );
eglTerminate( display );
display = EGL_NO_DISPLAY;
context = EGL_NO_CONTEXT;
surface = EGL_NO_SURFACE;
}
// -------- ここまでEGLの初期化と終了処理 --------