Windows上（Geforce）でOpenGL ES 3.0を試してみる。

## はじめに
OpenGL ES 3.0、始まってきましたね。Androidが4.3にて正式サポートを開始し、いよいよモバイルデバイスでもPlayStation 3 / Xbox 360世代を少し上回るような高度な表現が可能になってきました。
いずれ、近いうちにiOSも追従してサポートするでしょうし、今後モバイルデバイスで3Dを扱いたい人はぜひES 3.0をマスターしましょう。

OpenGL ES 3.0で何が変わったかについては、以下のサイトが詳しいです。
[組み込み向け3DグラフィックスAPIの最新版「OpenGL ES 3.0」が登場](http://www.kumikomi.net/archives/2012/09/ep11gles.php)

しかし、試そうにもES 3.0に対応したモバイルデバイスはまだまだ少ないのが実情です。できれば、Windows上で試したいですよね。実は、デスクトップ向けのOpenGL4.3に対応したGPUでは、OpenGL ES 3.0互換環境を利用することができます。
この記事では、その利用方法（OpenGL ES 3.0コンテキストの初期化方法）について解説します。

## 前提知識
OpenGLについて、ある程度なれている方が前提です。
また、WGLについても、多少の知識があることが望ましいです。

## 前提環境
* Window XP以降（筆者はWindows 7 64bit で動作確認）
* OpenGL4.3に対応したnVidia社製 GPU（筆者はGeforce GT 520で動作確認）

（GPUのドライバーは最新のバージョンをインストールしてください）

## 準備
[OpenGL Registry](http://www.opengl.org/registry/) から wglext.h をダウンロードし、インクルードします。

## コード説明

以下が初期化コードになります。
このコードだけじゃ全体像がわからないよ！　という方は、
こちらのサイト様のサンプルコードをご覧ください。
[OpenGL(WGL)サンプルプログラム on Windows7（fujinsブログ様）](http://blogs.yahoo.co.jp/simasaki2001/24983992.html)

以下のコードは、上記サイト様のサンプルコードの初期化コード部分を、私がOpenGL ES 3.0にあわせて改変したものになります。

ちなみにWGLとは、Windows依存のOpenGL関連機能（Windows向けのOpenGLレンダリングコンテキストを作成したり、OpenGLの拡張関数を取得するなど）を利用するためのAPIです。

処理の手順としては、次のようになります。
1. PIXELFORMATDESCRIPTOR型の変数として、ピクセルフォーマットを作成する。
2. ChoosePixelFormat() を使って、フォーマットのマッチングを行う。
3. 4のステップのために、通常の手段で 仮のContext を作成し wglMakeCurrent() でコンテキストを有効にする。
4. WGL の拡張 API 、wglCreateContextAttribsARB() を取得する。
5. wglCreateContextAttribsARB() を使って OpenGL ES 3.0 コンテキストを作る。
6. 3のステップで作った仮のコンテキストを削除する。

```cpp:
int initGL( HWND hwnd, HDC *p_hdc, HGLRC *p_hglrc ) {

	// ピクセルフォーマットの指定
	static PIXELFORMATDESCRIPTOR   pformat= {
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		0
		|PFD_DRAW_TO_WINDOW
		|PFD_SUPPORT_OPENGL
		|PFD_DOUBLEBUFFER
		,
		PFD_TYPE_RGBA,
		32,     // color
		0, 0,   // R
		0, 0,   // G
		0, 0,   // B
		0, 0,   // A
		0, 0, 0, 0, 0,      // AC R G B A
		24,     // depth
		8,      // stencil
		0,      // aux
		0,      // layertype
		0,  // reserved
		0,  // layermask
		0,  // visiblemask
		0   // damagemask
	};

	// デバイスコンテキストを取得する
	HDC hDC= GetDC( hwnd );

	// フォーマットのマッチングを行う
	int pfmt= ChoosePixelFormat( hDC, &pformat );

	// ピクセルフォーマットを設定
	SetPixelFormat( hDC, pfmt, &pformat );

	// wglCreateContextAttribsARBを取得するために、仮のコンテキストを取得
	HGLRC hGLRC_old = wglCreateContext( hDC );

	// 仮のコンテキストを有効にする
	wglMakeCurrent( hDC, hGLRC_old );

	// wglCreateContextAttribsARB 関数の取得
	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( "wglCreateContextAttribsARB" );

	// 取得したいOpenGL環境（今回はES 3.0）の指定
	static const int  opengl_es30[]= {
	  WGL_CONTEXT_MAJOR_VERSION_ARB,   3,
	  WGL_CONTEXT_MINOR_VERSION_ARB,   0,
	  WGL_CONTEXT_FLAGS_ARB,           0,
	  WGL_CONTEXT_PROFILE_MASK_ARB,    WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
	  0,
	};
	// ES3.0のコンテキストを取得
	HGLRC hGLRC= wglCreateContextAttribsARB( hDC, 0, opengl_es30 );

	// コンテキストを有効にする
	wglMakeCurrent( hDC, hGLRC );
	
	// 仮のコンテキストを削除
	wglDeleteContext( hGLRC_old );

	// ベンダー情報の取得
    const GLubyte *strVendor = glGetString(GL_VENDOR);
	std::cout << "GL_VENDOR : " << strVendor << std::endl;

	// レンダラー情報の取得
    const GLubyte *strRenderer = glGetString(GL_RENDERER);
	std::cout << "GL_RENDERER : " << strRenderer << std::endl;

	// バージョン情報の取得
    const GLubyte *strVersion = glGetString(GL_VERSION);
	std::cout << "GL_VERSION : " << strVersion << std::endl;

	// OpenGL GLSL バージョンの取得
	const GLubyte * strShaderLanguageVersion = glGetString (GL_SHADING_LANGUAGE_VERSION);
	std::cout << "GL_SHADING_LANGUAGE_VERSION : " << strShaderLanguageVersion << std::endl;

	// OpenGL拡張の取得
	const GLubyte * strExtensions = glGetString (GL_EXTENSIONS);
	std::cout << "GL_EXTENSIONS : " << strExtensions << std::endl;


	*p_hdc= hDC;
	*p_hglrc= hGLRC;

	return 0;
}
```

## 描画コードについて

ただし、上記サイト様のサンプルコードの描画処理や透視変換関数は、OpenGL ES 3.0とは互換性がないため、描画コードもろもろ（シェーダーや頂点オブジェクトの作成処理なども含む）については床井研究室様の以下のページで説明されているサンプルコードを利用してください。
[http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20120909](http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20120909)

[サンプルコード（main.cpp）への直リンク](https://github.com/tokoik/glfwsample/blob/65cc6ebe03fbef9ea4043c6fe8a88c233766f710/main.cpp)

ただし、このコードでも注意点が。Windowsでは、標準で使えるOpenGLの関数はバージョン1.1までのものしかなく、それ以外の最近の関数はwglGetProcAddress関数で取得する必要があります。
以下のように行ってください。

```cpp:
  PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress( "glGenVertexArrays" );
  PFNGLBINDVERTEXARRAYPROC glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress( "glBindVertexArray" );
```

また、シェーダープログラムについては、OpenGL ES 3.0に対応させるため、以下のコードで置き換えてください。

```c:
// バーテックスシェーダのソースプログラム
	static const GLchar vsrc[] =
	"#version 300 es\n"
    "in lowp vec4 pv;\n"
    "void main(void)\n"
    "{\n"
    "  gl_Position = pv;\n"
    "}\n";

  // フラグメントシェーダのソースプログラム
  static const GLchar fsrc[] =
	"#version 300 es\n"
    "out lowp vec4 fc;\n"
    "void main(void)\n"
    "{\n"
    "  fc = vec4(1.0, 0.0, 0.0, 0.0);\n"
    "}\n";
```

以上で、動作するはずです。以下のような描画が行えれば成功です。
![無題.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/1c3fe4ff-e1fe-f307-4367-70d141c8de90.jpeg)

## おわりに

一部のコードしか示さず、あとはあっちを参考にしてごにょごにょしろとか、おまいなめとんのか的な説明になってしまい大変申し訳ないです（汗）
私の手元で動いているコードを全部晒せれば手っ取り早いのですが、いろいろ汚いこともありまして、後日の課題とさせてくださいorz

ともかく、OpenGL4.3はOpenGL ES 3.0と互換性が確保されていますので、OpenGL4.3に対応するGPUならOpenGL ES 3.0のプログラム開発を行うことができます。
もちろん、本当にモバイルデバイスで動くかは、実機で検証した方がいいと思いますが、とっかかりの開発環境として使えると思います。