先日、久しぶりにOpenGL Extension Registryを見てたら、glcorearb.hなるファイルがありました。(気づくの遅い)
OpenGLの関数群について
現在のOpenGLの関数群は、大きく分けて、3種類に分かれます。
まずは、Vendor拡張とEXT拡張と呼ばれる関数群があります。これ等は、特定のGPUベンダーや、OS/PCメーカーなどが追加した機能拡張です。EXT拡張は、複数の会社が合意して提出された機能拡張です。大抵のOpenGLの最新機能は、これ等の拡張で定義された関数群で実装されます。
次は、ARB/KHR拡張と呼ばれる機能です。ARBは、OpenGL Architecture Review Boardのことで、OpenGLの機能を決める評議会と言えると思います。ここで承認された機能拡張は、ARB拡張となります。大抵は、まずはじめにVendor拡張や、EXT拡張として実装された機能が、一定の有意性が見出された場合、ARBの承認を得ることで、ARB拡張となることが多いです。KHRはKhronos Groupが主導して導入された機能拡張のようですが、ARBと同一の機能拡張番号を使用しているので、実質的にARB拡張とみなして良いと思います。
最後に、Coreと呼ばれる機能です。これは、OpenGLの標準機能に属します。これは、OpenGLのバージョンが上がるたびに、機能が変わりますが、ひとたび特定のバージョンをサポートしたDriver/GPUがあれば、それは、そのバージョンで定義されている機能を全てサポートしている必要があります。サポートする機能は、バージョンが上がるたびに、基本的には追加という形で増えていきます。大抵は、ARB拡張で定義されている機能拡張が、Coreの機能として取り込まれることが多いです。
しかし、OpenGL3.1/3.2では、機能の削減が行われました。(具体的には、一部のCore機能をARB拡張に移しました。)したがって、後方互換性を保障するものではありません。
OpenGLの機能拡張が分かりにくいワケ
上記のように、とあるひとつの機能が、Coreに取り込まれるまでの過程で、Vendor/EXT/ARB/Coreと複数の定義を持つことが多々あります。
そのため同一の機能が、複数の関数やdefineを持つこととなり、OpenGLの機能の分かりにくさを助長していると思います。
不要な機能拡張の使用を避けるため、OpenGLのプログラムを書く際は、OpenGL Extension Registryにアップされている、最新バージョンのCoreProfileSpecificationに沿って、プログラムを書くのが一番だと思います。そして、Coreに無い機能を使う場合には、まずARB拡張を探し、次にEXT拡張、最後にVendor拡張を探すと良いと思います。
ここでは、ひとつの分かりにくい例として、Occlusion Queryの機能について調べてみたいと思います。
Occlusion Query関連は、
- GL_HP_occlusion_test
- GL_NV_occlusion_query
- GL_ARB_occlusion_query
- GL_ARB_occlusion_query2
- OpenGL Core 1.5
- OpenGL Core 3.3
- OpenGL Core 4.3
で機能が追加されたり、Vendor->ARB->Coreへの変更などが行われています。下記にその内容を示します。
GL_HP_occlusion_test機能拡張
Occlusionの結果をBool値のみで取得可能。QueryがObject化されていないので、複数のOcclusionTestの結果を得るためには、一旦結果をCPU側で取得しなくてはならない。昔に作られた仕様。
GL_HP_occlusion_test #ifndef GL_HP_occlusion_test #define GL_HP_occlusion_test 1 #define GL_OCCLUSION_TEST_HP 0x8165 #define GL_OCCLUSION_TEST_RESULT_HP 0x8166 #endif /* GL_HP_occlusion_test */ 使い方 glEnable(GL_OCCLUSION_TEST_HP) //gl rendering calls glDisable(GL_OCCLUSION_TEST_HP) glGetBooleanv(GL_OCCLUSION_TEST_RESULT_HP, &result)
GL_NV_occlusion_query機能拡張
Occlusionの結果を描画されたサンプル数で取得する。QueryがObject化されているので、複数のOcclusionQueryの結果を、非同期的に取得することができる。CPU側で、即時に結果が読めるかどうかのチェックも可能。DX9の機能とほぼ同等。
GL_NV_occlusion_query #ifndef GL_NV_occlusion_query #define GL_NV_occlusion_query 1 #define GL_PIXEL_COUNTER_BITS_NV 0x8864 #define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 #define GL_PIXEL_COUNT_NV 0x8866 #define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); GLAPI void APIENTRY glEndOcclusionQueryNV (void); GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); #endif #endif /* GL_NV_occlusion_query */ 使い方 glBeginOcclusionQueryNV(occlusionQueries[i]); // render glEndOcclusionQueryNV(); // you can check if the result is already available for reading on CPU side. glGetOcclusionQueryuivNV(occlusionQueries[i], GL_PIXEL_COUNT_AVAILABLE_NV, &isAvailable); // retrieve the pixel count glGetOcclusionQueryuivNV(occlusionQueries[i], GL_PIXEL_COUNT_NV, &pixelCount);
GL_ARB_occlusion_query機能拡張
Vendor拡張がARB拡張になった典型的なケース。defineの名称や、関数名がARBに即した形になるが、同じ意味のdefineはNV拡張と同じ値。
GL_ARB_occlusion_query #ifndef GL_ARB_occlusion_query #define GL_ARB_occlusion_query 1 #define GL_QUERY_COUNTER_BITS_ARB 0x8864 #define GL_CURRENT_QUERY_ARB 0x8865 #define GL_QUERY_RESULT_ARB 0x8866 #define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 #define GL_SAMPLES_PASSED_ARB 0x8914 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); GLAPI void APIENTRY glEndQueryARB (GLenum target); GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); #endif #endif /* GL_ARB_occlusion_query */
GL_ARB_occlusion_query2機能拡張
ここで再びBool値でOcclusionの取得が可能になる。同時期にOpenGL Core3.3に取り込まれたため、本来ARBで定義されるべきdefineが無い。このように、Coreに取り込まれることが前提となるARB拡張では、ARBのpostfixがdefineや関数名についていないことがある。
GL_ARB_occlusion_query2 #ifndef GL_ARB_occlusion_query2 #define GL_ARB_occlusion_query2 1 #endif /* GL_ARB_occlusion_query2 */ GL_ANY_SAMPLES_PASSEDというdefineが新設されているが、Coreのdefineとして定義されている。
結果をBool値で欲しいとき(Bool値で十分なとき)は、GL_ANY_SAMPLES_PASSEDをQueryのBegin/Endで使用し、結果をBool値で受け取る。
OpenGL Core 1.5
GL_ARB_occlusion_queryに相当する機能がCore1.5で追加された。ARBのpostfixが取れている。
OpenGL Core 1.5 #ifndef GL_VERSION_1_5 #define GL_VERSION_1_5 1 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_SAMPLES_PASSED 0x8914 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); GLAPI GLboolean APIENTRY glIsQuery (GLuint id); GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); GLAPI void APIENTRY glEndQuery (GLenum target); GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); #endif #endif /* GL_VERSION_1_5 */
OpenGL Core 3.3
GL_ARB_occlusion_query2に相当する機能が追加された。必要なdefineが定義されている。
OpenGL Core 3.3 #ifndef GL_VERSION_3_3 #define GL_VERSION_3_3 1 #define GL_ANY_SAMPLES_PASSED 0x8C2F #endif /* GL_VERSION_3_3 */
OpenGL Core 4.3
MultiSample使用時のQueryのパフォーマンス向上のための機能追加が行われた。前提となる拡張が無く、いきなりCoreに取り込まれた。
OpenGL Core 4.3 #ifndef GL_VERSION_4_3 #define GL_VERSION_4_3 1 #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A #endif /* GL_VERSION_4_3 */
追加されたdefineは、MutisampleのRenderTargetを使用している時の、Ccclusionテストの高速化を実現するためのもの。ただし、保守的な方向で、実際とは異なる結果を返す可能性がある。(全てのsampleがOccludeされているにも関わらず、されていないと返す可能性があるが、はやくなる(かもしれない))
gl_corearb.hについて
上記で示したとおり、OpenGLの機能拡張で定義された関数やdefineを全て含んだ glext.h というヘッダファイルは、いわば二重定義の嵐のような状況になっています。
OpenGLの機能拡張に精通していれば、それほど迷うことは無いのですが、初めてOpenGLを学んだ方には、まさにカオティックな内容になっています。
また、OpenGL3.2で追加されたCoreProfileとCompatibilityProfileでは、使用可能な機能に違いがあるのですが、同一のヘッダファイルとライブラリファイルを使用するため、コンパイルは通るけど、使ってはいけない関数というものが存在していました。
この状況下で、OpenGLのCoreとARB拡張のみを取り出した、gl_corearb.h を新設したのはすばらしい事だと思います。gl_corearb.hに定義されるARB拡張は、OpenGLのCoreProfileと互換性のあるもののみで、CompatibilityProfileでのみ使用可能なARB拡張は定義されないそうです。詳しくは、OpenGL 4.3 Core Profile SpecificationのAppendix G.2に記載されています。
ちなみに、OcclusionQuery関連について、gl_corearb.hをチェックすると、GL_ARB_occlusion_queryはgl_corearb.hに入っていませんでした。しかし、GL_ARB_occlusion_query2は残っていました。なんだか上記の説明と異なる気もしますが、CoreProfileが定義されるOpenGL3.2以前にCoreに統合されたARB拡張も、削除されていると考えると納得できます。
WindowsでGLを御使用の方へ
Windows上でのOpenGL Extensionを使用する場合は、Perl等を使って、gl_ext.hを書き換えれば簡単に使用できる旨を以前の[記事]で書きましたが、gl_corearb.hも同様に、Perl等を使って加工することで簡単に使えると思います。
ただし、gl_corearb.hには、OpenGL1.0 Coreについて定義があり、ここで定義されている関数は既に存在します。一方その他の関数はwglGetProcAddress()で取得する必要があるので、OpenGL1.0 Coreの定義の部分だけ、気をつけて処理する必要があります。
一応2013/10現在のgl_ext.h, wgl_ext.h, gl_corearb.hをPerlで加工したものをUploadしておきます。
glcorearb_win-zip.jpg(保存して拡張子変えてください)
使い方は、プログラム中で一箇所のみ、関数とポインタの実体を作る必要があるので、以下のように記述します。
#define WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR 1 #define WINDOWS_GL_EXT_CREATE_FUNCTIONPTR 1 #include "glcorearb_win.h" #include "wglext_win.h" #undef WINDOWS_GL_EXT_CREATE_FUNCTIONPTR #undef WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR
それ以外の箇所で外部参照する際は、下記の様に記述します。
#define WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR 1 #include "glcorearb_win.h" #include "wglext_win.h" #undef WINDOWS_GL_EXT_DEFINE_FUNCTIONPTR
注:上記ファイルは、余り(殆ど)チェックをしていないので、なにか不具合があるかもしれません。