メモ代わり

Feed Rss

OpenGL ES Windows Emulator

06.29.2011, know how, by .

Setup

Android や iPhone などで使われている OpenGLES と同じような API が Windows でも使えるようです。昔あった Glide エミュレータみたいなものでしょうかね。

PowerVR SDK Download site

Windows 環境向けのものをダウンロードしてインストールします。インストール先ディレクトリ内にある、 TrainingCourse をこなすことで順番に機能を理解していく事ができます。とりあえず 01_HelloTriangle あたりをビルドしてみて動作確認をすると良いです。

DirectX と同じように環境変数を設定すると良いでしょう。 Builds/OGLES2 ディレクトリまで張ると良いです。手元の環境では、OGLES2_DIR D:\project\GLES2\Builds\OGLES2としました。 CMake を利用した場合、 DirectX SDK とあわせて次のように設定します。

if(MSVC)
include_directories($ENV{OGLES2_DIR}/Include $ENV{DXSDK_DIR}/include)
link_directories($ENV{OGLES2_DIR}/WindowsX86/Lib $ENV{DXSDK_DIR}/lib/x86)
endif(MSVC)

Open GL ES2 Overview

通常の OpenGL と比べると OpenGLES2 では固定シェーダは存在しません。シェーダのためのコードも書く必要があります。いわゆる頂点シェーダではポリゴンモデルの形状を変え、フラグメントシェーダでは表面を描画するためのプログラムを書きます。事前コンパイルされたシェーダを読み込ませる事もできるようですが、一般的には実行時にプログラムをコンパイルし、デバイスに与えます。ほかにもディスプレイリストなんかも無いようです。 DirectX で HLSL や VertexBuffer 、 IndexBuffer を使っている場合は特に混乱することなく移行できます。座標系なんかも関係ありません。

	// Fragment and vertex shaders code
	char* pszFragShader = "\
		void main (void)\
		{\
			gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\
		}";
	char* pszVertShader = "\
		attribute highp vec4	myVertex;\
		uniform mediump mat4	myPMVMatrix;\
		void main(void)\
		{\
			gl_Position = myPMVMatrix * myVertex;\
		}";

01_HelloTriangle より。これらの文字列をコンパイル関数に与えます。割と簡単です。 HLSL よりもシンプルです。

カメラの座標やモデルの位置などをアプリケーション側から設定し、シェーダプログラムで計算して画面上のどの位置に表示するか、どの色で描くかなどを設定します。

この OpenGLES API やシェーダプログラムを使って組んでおけばスマホへの移植が容易ということになります。

Coding

実際に SDK を使って画面クリアまでやってみます。

instance

状態として保持するもの。 config も保持しておいたほうがいいらしいですが、今回は必要ありませんでした。

EGLNativeWindowType nwt;
EGLNativeDisplayType ndt;
EGLDisplay display;
EGLSurface window_surface;

nwt はウィンドウハンドルと同等になります。同じアドレスを指します。
ndt はデバイスコンテキストになります。 GetDC で取得されます。
display と window_surface は初期化の経過で作られます。

initialize

初期化にはウィンドウハンドルが必要です。ウィンドウハンドルのクライアント領域を基準に作られるようなので、 AdjustWindowRectEx をして、キッチリウィンドウサイズを求めてから CreateWindow すると良いでしょう。

表示されたウィンドウハンドルを hwnd とします。

nwt=reinterpret_cast<EGLNativeWindowType>(hwnd);
ndt=reinterpret_cast<EGLNativeDisplayType>(GetDC(reinterpret_cast<HWND>(nwt)));

EGLint major_version;
EGLint minor_version;

display=eglGetDisplay(ndt);
if(!eglInitialize(display, &major_version, &minor_version))
{
    continue;
}
if(!eglBindAPI(EGL_OPENGL_ES_API))
{
    continue;
}

EGLint config_list[]={
    EGL_LEVEL,				0,
    EGL_SURFACE_TYPE,		EGL_WINDOW_BIT,
    EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
    EGL_NATIVE_RENDERABLE,	EGL_FALSE,
    EGL_DEPTH_SIZE,			24,
    EGL_NONE
};
EGLConfig config=static_cast<EGLConfig>(0);
EGLint num_config;
if(!eglChooseConfig(display, config_list, &config, 1, &num_config) || num_config != 1)
{
    continue;
}

window_surface = eglCreateWindowSurface(display, config, nwt, NULL);

EGLint context_attrib_list[]={
    EGL_CONTEXT_CLIENT_VERSION,2,
    EGL_NONE
};

EGLContext context;
context= eglCreateContext(display, config, NULL, context_attrib_list);

if(!eglMakeCurrent(display, window_surface, window_surface, context))
{
    continue;
}
eglSwapInterval(display,1);

continue が混ざっちゃってますが、エラーパスです。エラーが発生する事はほぼ無いと思われますが、終了パスへ進んでください。
コンフィグ系設定値は配列に type,value の順に詰め込んでいきます。 EGL_NONE が終端です。

loop

続いてメインループです。

glClearColor(0.2f, 0.2f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
eglSwapBuffers( display, window_surface);

glClearColor は設定が保持されるので、実は一度だけ呼べば OK です。 glClear に与える GL_COLOR_BUFFER_BIT についても、背景球や閉じられた空間では全面が描画されるため無駄になります。ステンシルバッファをクリアするときにもここでクリアしますが、ステンシルは使われなくなる傾向にあります。シェーダで似たような事ができるのでステンシルを使うのは避けましょう。深度だけクリアする感覚でよいと思います。

cleanup

そして終了処理です。

eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(display);
ReleaseDC(nwt,ndt);

result

実行するとやや青みがかった暗い画面になります。そうなれば OK です。