WindowsでOpenGLES --- ANGLE準備

WindowsでOpenGLESを使いたい場合は、Google開発のANGLEが利用できます。
GitHub - google/angle: A conformant OpenGL ES implementation for Windows, Mac and Linux.

ANGLEを利用すれば、OpenGLESで記述したものを、Direct3DやDesktop OpenGLレンダリングしてくれます。(現在デフォルトでDirect3D11を利用。eglGetPlatformDisplayEXTで変更可能)

今回はANGLEをビルドして、動作確認をしてみたいと思います。

ビルド

WindowsでビルドするにはGypを利用する必要がありますが、Microsoftが下記のリポジトリを作成してくれているので、こちらを利用します。
GitHub - Microsoft/angle: ANGLE: OpenGL ES to DirectX translation for Windows Store, Windows Phone and Desktop

ダウンロードしたら、src/angle.slnを開いてビルドします。
f:id:any-programming:20170419223925p:plain

gypフォルダの下にビルド結果が配置されます。

基本的に利用するのはlibEGL.dllとlibGLESv2.dllの2つです。
f:id:any-programming:20170419224050p:plain

ウィンドウ準備

今回はWin32APIでウィンドウを作成します。

ちなみにANGLEではウィンドウ(HWND)を準備することなくオフスクリーンレンダリングすることも可能です。

WNDCLASSEX wndclass;
wndclass.cbSize        = sizeof(WNDCLASSEX);
wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndclass.lpfnWndProc   = WndProc;
wndclass.cbClsExtra    = 0;
wndclass.cbWndExtra    = 0;
wndclass.hInstance     = GetModuleHandle(NULL);
wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName  = NULL;
wndclass.lpszClassName = L"GL Window";
wndclass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

RegisterClassEx(&wndclass);

HWND hwnd = CreateWindow(
    L"GL Window",
    L"GL Window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT, CW_USEDEFAULT,
    NULL,
    NULL,
    wndclass.hInstance,
    NULL);

ShowWindow(hwnd, SW_SHOWNORMAL);

WndProc

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}


OpenGLES初期化

display、config、contextの順に作成し、最後にsurfaceを作成しMakeCurrentを行います。

今回はES3.0を利用するよう初期化しています。

hwndは上記で作成したものを利用します。

#include <EGL\egl.h>
#pragma comment(lib, "libEGL.lib")

// display
EGLDisplay display = eglGetDisplay(GetDC(hwnd));
assert(display);

EGLint major, minor;
assert(eglInitialize(display, &major, &minor));

assert(eglBindAPI(EGL_OPENGL_ES_API));

// config
EGLint configAttributes[] =
{
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
    EGL_BUFFER_SIZE, 32,
    EGL_NONE
};
EGLConfig config;
EGLint num_config;
assert(eglChooseConfig(display, configAttributes, &config, 1, &num_config));
    
// context
EGLint contextAttributes[] =
{
    EGL_CONTEXT_CLIENT_VERSION, 3,
    EGL_NONE
};
EGLContext context = eglCreateContext(display, config, NULL, contextAttributes);
assert(context);

// surface
EGLSurface surface = eglCreateWindowSurface(display, config, hwnd, NULL);
assert(surface);

assert(eglMakeCurrent(display, surface, surface, context));


描画

WndProcのWM_PAINTに描画処理を追加します。

今回は背景を灰色に塗るだけです。

#define GL_GLEXT_PROTOTYPES
#include <GLES3\gl3.h>
#pragma comment(lib, "libGLESv2.lib")

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    glClearColor(0.5, 0.5, 0.5, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    assert(eglSwapBuffers(display, surface));

    EndPaint(hwnd, &ps);
}


Run

メッセージループを開始します。

灰色に塗りつぶされたウィンドウが表示されます。

MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

f:id:any-programming:20170420000211p:plain