月別アーカイブ: 2013年8月

OpenGL ES 3.0/OpenGL 4.x Uniform Block

Uniform Block は Direct3D の Constant Buffer に相当します。
外部で確保したメモリを、Shader からアクセス可能な Uniform 変数として
割り付けられるようになります。

トータルで Uniform として使えるメモリが増えますし、Shader 間での
データ共有も可能です。

OpenGL の Shader Program は D3D の FX に近い機能を持っていました。
シンボルは Shader 同士リンクされた状態となり、uniform の割り当てや
管理も自動的に行なってくれるため大変扱いやすくなっています。

その反面 GL 3.0 以前は uniform 値も Program Object 内で閉じており、
Camera や Light 等のシーンで共通なパラメータも、シェーダーそれぞれに
コピーする必要がありました。

OpenGL 3.1 と OpenGL ES 3.0 以降は予め Uniform Buffer を作って
おくことができます。
描画時は各シェーダーにバッファを bind するだけです。
その代わり、バッファ内の変数配置を自分で管理する必要があります。

// GLSL vsh
layout(std140) uniform Scene {
  mat4  Projection;
  mat4  ViewMatrix;
};

uniform mat4   World;
in vec3 POSITION;

void main()
{
   mat4 pvw= World * ViewMatrix * Projection;
   gl_Position.xyzw= vec4( POSITION.xyz, 1.0 ) * pvw;
}

↑シェーダー内では構造体のような書式で uniform block を宣言します。
HLSL の cbuffer と全く同じ仕様で、インスタンス名を宣言しなければ
シンボル名はトップレベルのスコープに含まれます。
main() の中でそのまま ViewMatrix や Projection を参照しています。

// C Init
struct SceneData {
  math::Matrix4 Projection;
  math::Matrix4 ViewMatrix;
};
GLuint scene_uniform= 0;
glGenBuffers( 1, &scene_uniform );
glBindBuffer( GL_UNIFORM_BUFFER, scene_uniform );
glBufferData( GL_UNIFORM_BUFFER, sizeof(SceneData), &scene_data, GL_STATIC_DRAW );
glBindBuffer( GL_UNIFORM_BUFFER, 0 );

GLuint scene_block_index= glGetUniformBlockIndex( program, "Scene" );
glUniformBlockBinding( program, scene_block_index, 5 );

↑Uniform Block の作成は通常の Buffer と同じです。
操作対象を glBindBuffer() を使って bind します。
管理しやすいように GLSL 側の block と同じ構造体を宣言しています。

// C Render
glUseProgram( program );
glBindVertexArray( input_layout );

// shader local
glUniformMatrix4fv( world_location, 1, GL_FALSE, matrix );

// global
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0 );

↑描画時の bind 命令は特殊です。

Uniform Block の API は、Texture Object や Sampler Object の
仕組みに似ています。

API 内部に Bind Point の Table が存在しており、Object はこの Table に登録します。
この場合の番号は任意で、利用可能な範囲内 (GL_MAX_UNIFORM_BUFFER_BINDINGS)
なら好きなものを選んで構いません。

glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

シェーダーにはこの Table の番号を登録します。

glUniformBlockBinding( program, scene_block_index, 5 );

Texture は glUniform1i() を使いましたが Uniform Block は専用の
命令 glUniformBlockBinding() があります。

Texture の場合と命令を比較すると下記の通り

● Texture の場合

// shader 側の Unit 指定
glUseProgram( program );
GLint loc= glGetUniformLocation( "SamplerName" );
glUniform1i( loc, 5 );
//  glGetUniformLocation() は 存在しない場合 -1 を返す

// object のバインド
glActiveTexture( GL_TEXTURE0 + 5 );
glBindTexture( GL_TEXTURE_2D, texture_object );
● Uniform Block の場合

// shader 側の Unit 指定
GLuint bindex= glGetUniformBlockIndex( program, "BlockName" );
glUniformBlockBinding( program, bindex, 5 );
//   glGetUniformBlockIndex() は 存在しない場合 GL_INVALID_INDEX を返す

// object のバインド
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

Texture Object では、Object 操作用の Bind と
描画用の Texture Image Unit 指定の Bind が同一でした。
複数の Unit を切り替えるためには別命令 glActiveTexture() を併用します。

Uniform Block の場合は、Object 操作用の Bind と描画用の BInd Point が
分かれています。

Object 操作は Vertex Buffer や Index Buffer などと同じ glBindBuffer() です。
描画用の Bind 登録には glBindBufferBase() か glBindBufferRange() を使います。
これらの命令は Uniform 以外の Buffer でも用います。

glBindBufferBase() や glBindBufferRange() を実行すると、描画用の
Bind Point だけでなく操作用の Bind Point の両方が置き換わるので注意が必要です。

glBindBuffer( GL_UNIFORM_BUFFER, 0 );

DumpUniformBinding();
//  Buf: Uni=0000            通常の glBindBuffer() の値 (0クリア)

glBindBufferBase( GL_UNIFORM_BUFFER, 0, scene_uniform );
glBindBufferBase( GL_UNIFORM_BUFFER, 4, scene_uniform );

DumpUniformBinding();
//  Buf: Uni=000e            通常の glBindBuffer() の値 (ここも設定される)
// [ 0]: Bind=000e (0 - 0)   glBindBufferBase( 0 ) の値
// [ 4]: Bind=000e (0 - 0)   glBindBufferBase( 4 ) の値

glBindBuffer( GL_UNIFORM_BUFFER, 0 );

DumpUniformBinding();
//  Buf: Uni=0000            通常の glBindBuffer() の値 (ここだけ 0)
// [ 0]: Bind=000e (0 - 0)   glBindBufferBase( 0 ) の値
// [ 4]: Bind=000e (0 - 0)   glBindBufferBase( 4 ) の値

上記のように glBindBufferBase() は通常の glBindBuffer() の bind point も書き換えます。
ですが glBindBuffer() を実行しても、描画用の Bind Point には影響が出ません。

よって下記のように glDrawElements() の前に
glBindBuffer( GL_UNIFORM_BUFFER, 0 ) を入れても問題ありません。

glUseProgram( program );
glBindVertexArray( input_layout );
glUniformMatrix4fv( world_location, 1, GL_FALSE, matrix );
glBindBufferBase( GL_UNIFORM_BUFFER, 5, scene_uniform );

glBindBuffer( GL_UNIFORM_BUFFER, 0 ); // 問題なし

glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0 );

なお glBindBufferBase() でバッファを登録しても GL_UNIFORM_BUFFER_SIZE
は 0 のままでした。RADEON だけ値が入っています。

GPU                    GL_UNIFORM_BUFFER_SIZE
---------------------------------------------
Intel HD 4000      4.0          0
GeForce GTX 650    4.4          0
RADEON HD 6750M    4.3        128
Mali-T604   OpenGL ES 3.0       0

下記は Uniform Block の利用可能なサイズを調べた結果です。

                        Intel  GeForce  RADEON    RADEON
             Mali-T604  HD4000 GTX650M  HD6750M   HD7750(GCN)
------------------------------------------------------------
Vertex:
 Components     1024     4096     4096    16384     1024
 Blocks           12       12       14       15       15
 Combined      50176    53248   233472   262144   246784

Fragment:
 Components     4096     4096     2048    16384     1024
 Blocks           12       12       14       15       15
 Combined      53248    53248   231424   262144   246784

Hull (tcsh):
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Domain (tesh):
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Geometry:
 Components              4096     2048    16384    16384
 Blocks                    12       14       15       15
 Combined               53248   231424   262144   246784

Compute:
 Components                       2048     1024     1024
 Blocks                             14       16       16
 Combined                       231424    32768     1024

Bindings          36       60       84       75       75
BlockSize      16384    16384    65536    65536    65536
CombindBlocks     24       60       84       75       75

Direct3D API では shader 毎に 4096 elements の block を 14 slot まで
利用できます。(OpenGL の components と blocks に相当)
GL API では上記のように GPU 毎に異なる値となっています。

関連エントリ
OpenGL の各バージョンと GLSL の互換性
OpenGL のエラー判定と OpenGL 4.3 Debug Output
OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object
OpenGL ES 3.0 と Vertex Array Object

OpenGL の各バージョンと GLSL の互換性

OpenGL のシェーディング言語 GLSL は、OpenGL の API と同様に
活発な更新が行われています。
Direct3D の HLSL よりも種類が多く、バージョン間には機能差があります。

GPU に関する機能だけでなく、言語の構文なども拡張が続けられているようです。
いくつかの機能に関して GLSL バージョン間の違いをまとめてみました。

GLSL Version

下記の表は部分的にピックアップしています。

// GL2 Group
OpenGL        GLSL         version 行      suffix   ITC
--------------------------------------------------------
OpenGL ES 2.0 GLSL ES 1.0  #version 100    -        -
OpenGL 2.0    GLSL 1.1                     -        -
OpenGL 2.1    GLSL 1.2     #version 120    f        ◎
// GL3/4 Group
OpenGL        GLSL        version 行       suffix  ITC  UBlock
----------------------------------------------------------------
OpenGL 3.0    GLSL 1.3	  #version 130     u,f      ◎  -
OpenGL 3.1    GLSL 1.4	  #version 140     u,f      ◎  U
OpenGL 3.2    GLSL 1.5	  #version 150     u,f      ◎  U,I,O
OpenGL ES 3.0 GLSL ES 3.0 #version 300 es  u,f      -   U
OpenGL 3.3    GLSL 3.3	  #version 330     u,f      ◎  U,I,O
OpenGL 4.0    GLSL 4.0	  #version 400     u,f,lf   ◎  U,I,O
OpenGL 4.1    GLSL 4.1	  #version 410     u,f,lf   ◎  U,I,O
OpenGL 4.2    GLSL 4.2	  #version 420     u,f,lf   ◎  U,I,O
OpenGL 4.3    GLSL 4.3	  #version 430     u,f,lf   ◎  U,I,O,B
OpenGL 4.4    GLSL 4.4	  #version 440     u,f,lf   ◎  U,I,O,B

GLSL は上位互換性を保っており、コンパイラは
1行目の「#version」行でバージョンを判定しています。

まず OpenGL 2.x 系と OpenGL 3.0 以降で大きな違いがあります。
attribute/varying と in/out のキーワードが変更され、
texture2D() 等の関数名も変わりました。

uint/uvec (unsigned int) や switch 文が使えるようになったのも
OpenGL 3.0 (GLSL 1.3) からです。

double 型の対応は OpenGL 4.0 (GLSL 4.0) 以降。
Uniform Block (Constant Buffer) は OpenGL 3.1 (GLSL 1.4) で追加されましたが、
in/out の Interface Block は 3.2 (GLSL 1.5) で対応しました。

GLSL は C言語や他のシェーダー言語と違い、初期化にはコンストラクタが必要でした。

// GLSL
vec3  pos= vec3( 1.0, 2.0, 3.0 );
float a[4]= float[]( 1.0, 2.0, 3.0, 4.0 );

OpenGL 4.2 (GLSL 4.2) 以降は C言語ライクな初期化リスト {~} が使えるように
なっており下記の書式が通ります。

// GLSL
vec3  pos= { 1.0, 2.0, 3.0 };
float a[4]= { 1.0, 2.0, 3.0. 4.0 };

予想に反して OpenGL 4.0 の Intel HD 4000 (9.18.10.3165) でもコンパイルが通りました。

OpenGL ES 3.0 はいくつか仕様が削られているようです。
特に暗黙の型変換ができません。↓がエラー。

float a= 1;   //  int to float

OpenGL ES 2.0 でも自動変換出来なかったので、この場合は 1.0 と記述する
必要がありました。
(OpenGL ES 2.0 だと suffix も使えないので 1.0f と書いてもエラー)

OpenGL ES 3.0 は符号なし整数型でも同様の問題が起こります。

uint  i= 32;   //  int to uint

下記のように ‘u’ が必要です。

uint  i= 32u;

暗黙の型変換があるとオーバーロード関数の検索も一気に複雑になるので、
組み込み用途では不要だと判断したのかもしれません。
ただしコンパイラがきちんとエラーを返してくれるので、
これらの問題はすぐに判明します。

●互換性

HD 4000 で Initializer List が使えたので他の GPU でも実験してみました。

GPU              OpenGL      GLSL version    compile
----------------------------------------------------
Intel HD 4000    OpenGL 4.0  #version 400      OK
RADEON HD 6750M  OpenGL 4.3  #version 430      OK
RADEON HD 6750M  OpenGL 4.3  #version 400      OK
GeForce GTX 650  OpenGL 4.4  #version 440      OK
GeForce GTX 650  OpenGL 4.4  #version 400     Error

GLSL の version を下げると GeForce ではエラーになりますが
RADEON では通りました。
GLSL の仕様上はエラーの方が正解です。
A では動くが他社の B では動かないといった違いは、意外にこの辺りが
原因なのかもしれません。

OpenGL のエラー判定と OpenGL 4.3 Debug Output

OpenGL のエラー状態を調べるには glGetError() を使います。
3~5種類程度のエラーコードが得られるので、マニュアルを頼りに
API 毎に発生条件を照らし合わせていきます。

どのタイミングで発生したのか特定するには、OpenGL の命令呼び出し毎に
glGetError() をチェックしなければなりません。

● glGetError() の判定

glActiveTexture( GL_TEXTURE0 + 32 );
GLenum error_code= glGetError();
if( error_code != GL_NO_ERROR ){
   // ERROR
}

↑これを毎回手で行うのは大変なので、何らかの形で自動化します。
現在は、下記のようなヘッダを作って対処しています。

// GLErrorLayer_ES2.h
// __gl2_h_
inline void ActiveTexture( GLenum texture )
{
    glActiveTexture( texture );
    FL_GL_ERROR_TRAP( "glActiveTexture" );
}
inline void AttachShader( GLuint program, GLuint shader )
{
    glAttachShader( program, shader );
    FL_GL_ERROR_TRAP( "glAttachShader" );
}
~
inline GLint GetAttribLocation( GLuint program, const GLchar * name )
{
    GLint ret= glGetAttribLocation( program, name );
    FL_GL_ERROR_TRAP( "glGetAttribLocation" );
    return  ret;
}
~

GLES2/gl2.h, GLES3/gl3.h, GL4/glcorearb.h などのヘッダファイルを元に
スクリプトで自動生成しています。
下記のように include して使います。

// GLErrorLayer.h
namespace el {

#if flDEBUG
# define  FL_GL_ERROR_TRAP( func_name )	gw::ErrorHandle( func_name )
#else
# define  FL_GL_ERROR_TRAP( func_name )
#endif

#include  "gw/entry/GLErrorLayer_ES2.h"
}

↑コンパイル時にエラー判定を行うかどうか選択できます。
実際のエラー判定は下記のような関数です。

bool ErrorHandle( const char* name )
{
    GLenum   error_code= glGetError();
    if( error_code == GL_NO_ERROR ){
        return  true;
    }
    do{
        const char* msg= "";
        switch( error_code ){
        case GL_INVALID_ENUM:      msg= "INVALID_ENUM";      break;
        case GL_INVALID_VALUE:     msg= "INVALID_VALUE";     break;
        case GL_INVALID_OPERATION: msg= "INVALID_OPERATION"; break;
        case GL_OUT_OF_MEMORY:     msg= "OUT_OF_MEMORY";     break;
        case GL_INVALID_FRAMEBUFFER_OPERATION:  msg= "INVALID_FRAMEBUFFER_OPERATION"; break;
        default:  msg= "Unknown"; break;
	}
        FL_ERROR( "GLErrorLayer:ERROR:%04x'%s' %s\n", error_code, msg, name );
        error_code= glGetError();
    }while( error_code != GL_NO_ERROR );
    FL_ABORT();
    return  false;
}

利用時は el:: をつけて下記のような感じです。

el::ActiveTexture( GL_TEXTURE );

これはエラー判定の一例ですが、楽をしたければそれなりの準備が必要で、
結構手間もかかります。

生成したヘッダは下記の場所に置いてみました。

 ・OpenGL ES 2.0 : GLErrorLayer_ES2.h
 ・OpenGL ES 3.0 : GLErrorLayer_ES3.h
 ・OpenGL 4.4 : GLErrorLayer_GL4.h

● Debug Output (OpenGL 4.3)

OpenGL のエラー出力にはいくつかの例外があります。
例えば Shader API では、Compiler や Linker のエラー内容をプログラマ向けの
テキスト情報で受け取ることができます。

同じように OpenGL 4.3 では、API のエラーメッセージを
わかりやすい形↓で受け取れるようになっています。

// GeForce GTX 650 (OpenGL 4.4 beta)
GL_INVALID_VALUE error generated. Element exceeds maximum supported number of combined texture image units.

GL_INVALID_ENUM error generated.  enum is invalid; expected GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TEXTURE_WRAP_R, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_GENERATE_MIPMAP, GL_TEXTURE_COMPARE_MODE, etc. (19 others).
// RADEON HD 7750 (OpenGL 4.3 beta)
glActiveTexture has generated an error (GL_INVALID_ENUM)

glTexParameteri parameter  has an invalid enum '0x285a' (GL_INVALID_ENUM)

Debug Output はちょうど Direct3D で言えば Debug Layer に相当します。
Direct3D 10 以降は GPU が汎用化されてできることが増えたのですが、
それでも GPU は CPU と違ってリソースの扱いでもさまざまな制約があります。

ちょっとした細かい設定やフラグの組み合わせなど、気付きにくいようなミスも
D3D Debug Layer は (少々うるさいくらいに) 報告してくれました。
デバッグだけでなく機能の理解にも役に立ちました。

OpenGL の場合はいくつかの準備が必要です。

1. Context 作成時に WGL_CONTEXT_FLAGS_ARB に WGL_CONTEXT_DEBUG_BIT_ARB を設定
2. glDebugMessageCallbackARB() で callback 関数を登録する

RADEON の場合下記のように GL_VERSION 文字列も変化します。

GL_VERSION: 4.3.12438 Core Profile/Debug Context 13.200.0.0
GL_RENDERER: AMD Radeon HD 7700 Series
GL_VENDOR: ATI Technologies Inc.
GL_SHADING_LANGUAGE_VERSION: 4.30
static int  att_command[]= {
    WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
    WGL_CONTEXT_MINOR_VERSION_ARB, 3,
    WGL_CONTEXT_FLAGS_ARB,         WGL_CONTEXT_DEBUG_BIT_ARB,
    WGL_CONTEXT_PROFILE_MASK_ARB,  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
    0
};
hrc= wglCreateContextAttribsARB( hDC, 0, att_command );

wglMakeCurrent() で OpenGL API が使えるようになったら Callback 関数を
登録します。

glDebugMessageCallback( DebugCallback, reinterpret_cast( this ) );
glEnable( GL_DEBUG_OUTPUT );

Debug Context はデフォルトで glEnable( GL_DEBUG_OUTPUT ) になるので
2行目は無くても構いません。

OpenGL の命令でエラーが発生すると Callback 関数が呼ばれます。

void GWModule::DebugCallbackFunc( GLenum source, GLenum type, GLuint eid, GLenum severity, GLsizei length, const GLchar* message, const void* user_param )
{
    ~
    FL_LOG( "%s\n", message );
}

手間もかからずにエラー内容も発生場所もわかるので、非常に便利になりました。

● OpenGL 4.2

GL_ARB_debug_output があれば同様のエラー情報を利用することができます。
関数名の最後に ARB が付くだけです。
RADEON HD 6570M OpenGL 4.2 で動作を確認できました。

● OpenGL ES 3.0

Mali-T604 では、Android 4.3 の OpenGL ES 3.0 利用時に同様の機能
GL_KHR_debug があります。

いろいろ試したのですが、eglGetProcAddress() でも
glDebugMessageCallbackKHR() などの関数を取ることができませんでした。
この辺りはもう少し調べたいと思っています。

関連エントリ
OpenGL のはじめ方

OpenGL ES 3.0/OpenGL 4.4 Texture Object と Sampler Object

Buffer と同じように Texture を扱う場合も Bind 操作が必要です。
Texture Object の Bind は二つの意味に用いられます。

 1. Texture Object を操作する場合
 2. 描画で用いる Texture Image Unit に割り当てる場合

Texture Object の種類(タイプ)は下記の通り。

                                 ES2 ES3 GL30 GL31 GL32 GL4
------------------------------------------------------------
GL_TEXTURE_1D                     -   -   ◎   ◎   ◎   ◎
GL_TEXTURE_2D                     ◎  ◎  ◎   ◎   ◎   ◎
GL_TEXTURE_CUBE_MAP               ◎  ◎  ◎   ◎   ◎   ◎
GL_TEXTURE_3D                     -   ◎  ◎   ◎   ◎   ◎
GL_TEXTURE_1D_ARRAY               -   -   ◎   ◎   ◎   ◎
GL_TEXTURE_2D_ARRAY               -   ◎  ◎   ◎   ◎   ◎
GL_TEXTURE_2D_MULTISAMPLE         -   -   -    -    ◎   ◎
GL_TEXTURE_2D_MULTISAMPLE_ARRAY   -   -   -    -    ◎   ◎
GL_TEXTURE_CUBE_MAP_ARRAY         -   -   -    -    -    ◎
GL_TEXTURE_RECTANGLE              -   -   -    ◎   ◎   ◎
GL_TEXTURE_BUFFER                 -   -   -    ◎   ◎   ◎

glBindBuffer() と同じように glBindTexture() にタイプを指定します。
タイプの数だけ Bind 状態が保持されます。

● 1. Texture Object を操作する場合

GLuint tex_obj1= 0;
GLuint tex_obj2= 0;

glGenTextures( 1, &tex_obj1 );
glGenTextures( 2, &tex_obj2 );

glBindTexture( GL_TEXTURE_2D, tex_obj1 );
glBindTexture( GL_TEXTURE_3D, tex_obj2 );

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glBindTexture( GL_TEXTURE_2D, 0 );
glBindTexture( GL_TEXTURE_3D, 0 );

異なるタイプの Texture Object は同時に Bind できます。
それぞれ glTexParameteri() や glTexImage 等の命令を実行することが可能です。

● 2. 描画で用いる Texture Image Unit に割り当てる場合

描画時にシェーダーが参照する Texture も Bind で渡します。

シェーダーは一度に複数のテクスチャにアクセスできます。
まずは割り当てたい Texture Unit (Texture Image Unit) の番号を
先に指定しておきます。

glActiveTexture( GL_TEXTURE0 ); // Texture Image Unit 0
glBindTexture( GL_TEXTURE_2D, texture_object1 );

glActiveTexture( GL_TEXTURE1 ); // Texture Image Unit 1
glBindTexture( GL_TEXTURE_2D, texture_object2 );

仮にアクセスできる Unit が 8個あるなら、glActiveTexture() の指定も
GL_TEXTURE0 ~ GL_TEXTURE7 の範囲になります。
シェーダー側では Sampler (Sampler2D 等) に設定した glUniform1i() の値で
これらのテクスチャを区別するわけです。

glActiveTexture() の動作を調べてみると、現在の Bind 状態がごっそりと
入れ替わっていることがわかります。

// (1)
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, tex_obj1 );
glBindTexture( GL_TEXTURE_CUBE_MAP, tex_obj2 );

glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, tex_obj1 );
glBindTexture( GL_TEXTURE_3D, tex_obj3 );

glActiveTexture( GL_TEXTURE3 );
glBindTexture( GL_TEXTURE_3D, tex_obj4 );

例えば OpenGL ES 3.0 で Total (Combined) の Unit が 32個ある場合、
GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP,
GL_TEXTURE_2D_ARRAY の 4種類のバインド状態が
32個 (4 x 32 = 128) まで保持されることになります。
(1) の実行結果をまとめると下記の表の通り。

      glActiveTexture= 0     1     2     3     4     5     6  ... 31
---------------------------------------------------------------------
GL_TEXTURE_2D         obj1  obj1   --    --    --    --    --     --
GL_TEXTURE_CUBE_MAP   obj2   --    --    --    --    --    --     --
GL_TEXTURE_3D          --   obj3   --   obj4   --    --    --     --
GL_TEXTURE_2D_ARRAY    --    --    --    --    --    --    --     --

ただし、実際の描画で Shader が参照できる Texture Object は
各 Unit 1つにつき 1つだけです。

例えば上の状態でシェーダー内の uniform Sampler2D と SamplerCube の両方が、
Unit 0 ( glActiveTexture( GL_TEXTURE0 ) ) の obj1 と obj2 を
それぞれ参照することはできません。
同じ Unit を参照している場合 glValidateProgram() が失敗します。

まとめると、
1. の用途では同時に複数の Texture Object を Bind することがありますが、
2. の用途では、現在の ActiveTexture (Texture Image Unit) に対して複数
Texture を Bind することはありません。

Bind は複数の用途に用いられるので注意が必要です。
2. の用途で Bind している間に、画像サイズを調べようとして 1. の
つもりで Bind してしまうと状態を破壊してしまうことになります。

● Texture Image Unit

GPU には複数の Image Unit が搭載されています。
シェーダーは Unit の数だけ、同時に異なるテクスチャから画像を
読み込むことができます。

各 Unit には Sampler と Texture Object の二つの状態を割り当てます。

 ・Sampler : データの読み込み方法
 ・Texture Object : データそのもの

例えば Fragment Shader (PixelShader) の Texture Image Unit 数
(GL_MAX_TEXTURE_IMAGE_UNITS) が 16個なら、FragmentShader は同時に 16種類の
異なるテクスチャマップにアクセスできます。

uv を変更して同じテクスチャを何度も読み込む場合は特に回数制限はありません。
ShaderModel 2.0 (D3D9) 以降は Sampler と Texture の Load 命令の実行が
分離されているからです。(ShaderModel 1.x までは同一でした)

最小 Unit 数
                 vsh   fsh   gsh   tcsh  tesh  csh  total
----------------------------------------------------------
OpenGL   2.0      0     2     -     -     -     -     2
OpenGL   2.1      0     2     -     -     -     -     2
OpenGLES 2.0      0     8     -     -     -     -     8
OpenGL   3.0     16    16     -     -     -     -    16
OpenGLES 3.0     16    16     -     -     -     -    32
OpenGL   3.1     16    16     -     -     -     -    32
OpenGL   3.2     16    16    16     -     -     -    48
OpenGL   3.3     16    16    16     -     -     -    48
OpenGL   4.0     16    16    16    16    16     -    80
OpenGL   4.1     16    16    16    16    16     -    80
OpenGL   4.2     16    16    16    16    16     -    80
OpenGL   4.3     16    16    16    16    16    16    96
OpenGL   4.4     16    16    16    16    16    16    96
----------------------------------------------------------
・total = GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS

少なくても上記個数は使用可能となっています。
ただ実際に調べたところ、GPU によっては GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
(total) が必ずしも上記表の最低個数を満たしているとは限らないようです。

                         OpenGL     v/ f/ g/tc/te/csh    total
------------------------------------------------------------
Adreno 320(Android4.1)   ES 2.0     4/16/--/--/--/--    20
Mali-T604 (Android4.3)   ES 3.0    16/16/--/--/--/--    32
Intel HD 4000 x86           4.0    16/16/16/16/16/--    80
RADEON HD 6750M x64         4.2    16/16/16/16/16/--    32 *
GeForce GTX 650 x64         4.4    32/32/32/32/32/32   192

描画に使うテクスチャは、glDrawElements() 等の描画命令発行時に Bind された
状態になります。
Fragment Shader だけでなく、Vertex, Geometry 等の各シェーダーがそれぞれ
異なる Texture にアクセスすることが可能です。

例えば vsh, fsh, gsh が 16種類のすべて異なるテクスチャを参照する場合は
48個の Bind 状態が必要になります。

glActiveTexture() に指定する GL_TEXTRE0~ のシンボルは OpenGL 4.4 の
ヘッダを見ても GL_TEXTRE0 ~ GL_TEXTRE31 の 32個しか定義されていません。

OpenGL では GL_TEXTRE0 のオフセット指定が有効だと明記されているので、
32以上も下記の方法でアクセスすることが出来ます。

glActiveTexture( GL_TEXTURE0 + 32 );
...
glActiveTexture( GL_TEXTURE0 + 47 );

ちなみに OpenGL 4.2 (GLSL 4.2) 以降は Shader 内でも直接 Texture Image Unit
の番号を設定できるようになっています。

// GLSL
layout(binding=1) uniform sampler2D    ColorMap;
layout(binding=2) uniform sampler2D    NormalMap;
layout(binding=0) uniform samplerCube  ReflectionMap;

layout の binding は、今まで外部から glUniform1i() で与えていた番号に相当します。

● Sampler Object

Texture の Wrap や Filter mode 等の Sampler State は、Direct3D の場合は
Texture Stage (Sampler) 側の設定値でした。
OpenGL ES 2.0 の場合は Texture Object がこれらのステートを所有します。

例えば下記のように同じオブジェクトを 0,1 の Unit に割り当てます。

// OpenGL ES 2.0
glBindTexture( GL_TEXTURE_2D, tex_obj1 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, tex_obj1 );

glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, tex_obj1 );

// -- (2)
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, tex_obj1 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

(2) で Unit 0 にバインドされたテクスチャの、Filter mode を変更すると、
Unit 1 の設定値も GL_LINEAR に書き換わります。

OpenGL ES 3.0/OpenGL 3.0 以降は Sampler Object が導入され、D3D のように
Image Unit に直接これらのステートを設定できるようになりました。

各パラメータは Sampler Object 優先です。
Image Unit に Sampler Object が設定されていない場合のみ
Texture Object の値が用いられます。

GLuint sampler_obj= 0;
glGenSamplers( 1, &sampler_obj );
glSamplerParameteri( sampler_obj, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glSamplerParameteri( sampler_obj, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

~
glBindSampler( 0, sampler_obj );

Sampler API は、パラメータの設定時は glActiveTexture() の影響を受けません。
また GL_TEXTURE0 ~ ではなく、直接 Unit 番号 0~ を指定しています。

Texture Object と違い、Object 操作のための Bind (1. 相当) が不要です。
Bind するのは実際に Image Unit に割り当てる場合だけ (2. 相当) なので、
わかりやすくてミスしづらい構造となっています。

OpenGL 4.4 ではさらに、複数の Sampler Object をまとめて Bind できる
glBindSamplers() 命令が追加されました。
これは Direct3D 10/11 の SetSamplers() 系命令によく似ています。

GL_SAMPLER_BINDING を用いて現在の Bind 状態を参照する場合は
glActiveTexture() も影響しています。

glActiveTexture( GL_TEXTURE0 + 0 );
GLint sampler= 0;
glGetIntegerv( GL_SAMPLER_BINDING, &sampler );

この値は glActiveTexture() で切り替わるのですが、RADEON だけ
値が取れませんでした。

Mali-T604         ES 3.0   取れる
Intel HD 4000        4.0   取れる
RADEON HD 6750M      4.2   取れない (追記 driver 更新で正常動作を確認)
GeForce GTX 650      4.4   取れる

2013/08/11 追記: RADEON HD 6750M でも Catalyst 13.8(beta) OpenGL 4.3 で正しく状態を取れることを確認しました。

関連エントリ
OpenGL ES 3.0 / OpenGL 4.3 VertexArrayObject と VertexAttribBinding

OpenGL ES 3.0 / OpenGL 4.3 VertexArrayObject と VertexAttribBinding

OpenGL API のオブジェクト操作は、必ずライブラリ内の global な変数を
経由して行います。
例えば Buffer Size を調べる場合も、現在の context のステートに
オブジェクトを bind してから対応するメソッドを呼び出します。

// GL2
glBindBuffer( GL_ARRAY_BUFFER, vertex_object );
GLint	param[1];
glGetBufferParameteriv( GL_ARRAY_BUFFER, GL_BUFFER_SIZE, param );
glBindBuffer( GL_ARRAY_BUFFER, 0 );

オブジェクトが導入される前の API と互換性を保つことができる反面、
どのオブジェクトを対象としているのかコードを見ただけではわからなく
なっています。

どのオブジェクトが必要なのか、どこまで影響を与えるのかは、それぞれ
命令仕様をマニュアル等で確認する必要があります。

例えば OpenGL ES 2.0 の描画のコードは下記の通りです。

// GL2 -- (1)
glUseProgram( program );
glUniform4fv( location, 4, matrix );
glBindBuffer( GL_ARRAY_BUFFER, vertex_object );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 24, (void*)0 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 24, (void*)12 );
glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );
glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0 );

もし仮に D3D API のような書き方ができるとしたらこんな↓感じになるでしょうか。

// GL2 -- 擬似コード (2)
program->SetConstant( location, 4, matrix );
device_context->SetProgram( program );
device_context->SetIndexBuffer( index_object );
device_context->SetVertexAttrib( 0, 3, GL_FLOAT, GL_FALSE, 24,  0, vertex_object);
device_context->SetVertexAttrib( 1, 3, GL_FLOAT, GL_FALSE, 24, 12, vertex_object);
device_context->DrawIndexed( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0 );

「擬似コード (2)」を見ると device_context に SetVertexBuffer() 命令が無く、
SetVertexAttrib() の方に vertex_object を渡していることがわかるかと思います。

実際に (1) の glDrawElements() は glBindBuffer( GL_ARRAY_BUFFER ) を
見ておらず、直前に glBindBuffer( GL_ARRAY_BUFFER, 0 ) があっても動作します。

glBindBuffer( GL_ARRAY_BUFFER ) を必要とするのは glVertexAttribPointer()
の方で、各 Vertex Attribute が頂点バッファの情報を保持しています。
Attribute それぞれが異なる Vertex Buffer を参照している可能性があるからです。

やはり (1) のコードを見ただけではこのような内部構造がわからないので、
OpenGL 命令の仕様はきちんと確認した方が良いようです。

● Vertex Array Object

頂点のバインドは複雑で情報も多いので、OpenGL 3.x 以降や OpenGL ES 3.0 では
Vertex Array Object (VAO) が導入されています。

// GL3 -- Vertex Array Object の作成 (3)
GLuint input_layout= 0;
glGenVertexArrays( 1, &input_layout );

glBindVertexArray( input_layout );
glBindBuffer( GL_ARRAY_BUFFER, vertex_object );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 24, (void*)0 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 24, (void*)12 );
glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );
glBindVertexArray( 0 );

VAO は頂点 Attribute の情報を保存し、描画時に利用することができます。

// GL3 -- VAO を使った Rendering (4)
glUseProgram( program );
glUniform4fv( location, 4, matrix );
glBindVertexArray( input_layout );
glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0 );

OpenGL 3.x (core profile) 以降は VAO が必須で、glBindVertexArray() が無いと
glVertexAttribPointer() がエラーになります。

つまり glVertexAttribPointer() は GL2 までは device_context のメソッドでしたが、
GL3 からは Vertex Array Object のメソッドに相当します。
擬似コードで表現してみます。

// GL3 -- VAO 作成 擬似コード (5)
IInputLayout* input_layout= device->CreateInputLayout( 2 );
input_layout->SetIndexBuffer( index_object );
input_layout->SetVertexAttrib( 0, 3, GL_FLOAT, GL_FALSE, 24,  0, vertex_object );
input_layout->SetVertexAttrib( 1, 3, GL_FLOAT, GL_FALSE, 24, 12, vertex_object );
// GL3 -- VAO を使った Rendering 擬似コード (6)
program->SetConstant( location, 4, matrix );
device_context->SetProgram( program );
device_context->SetInputLayout( input_layout );
device_context->DrawIndexed( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0 );

OpenGL ES 3.0 では OpenGL ES 2.0 互換性のために VAO 無しでも
glVertexAttribPointer() が使えるようになっています。
bind == 0 時にデフォルトの VAO が割り当てられていると考えられます。
OpenGL 3.x でも、RADEON など一部 GPU では default VAO が有効なものが
あるようです。

Vertex Array Object は Direct3D の InputLayout や VertexDeclaration に
相当しますが、上記のように vertex_object や index_object が含まれている点で
異なっています。
D3D では頂点のバインド情報とバッファの割り当ては別の命令でした。
OpenGL でも 4.3 で D3D 同様の仕組みが導入されているようです。
(後述: Vertex Attrib Binding)

● GL_ELEMENT_ARRAY_BUFFER の扱い

VAO の index_object に対する挙動は vertex_object の場合とは異なっています。

glDrawElements() は glBindBuffer( GL_ARRAY_BUFFER ) を参照しないし
VAO も glBindBuffer( GL_ARRAY_BUFFER ) に影響を与えません。

ですが、glDrawElements() は glBindBuffer( GL_ELEMENT_ARRAY_BUFFER ) を
参照し、VAO は glBindBuffer( GL_ELEMENT_ARRAY_BUFFER ) を置き換えます。

よって直前で glBindBuffer( GL_ELEMENT_ARRAY_BUFFER ) に 0 が
書き込まれると glDrawElements() は失敗します。

GLuint  input_layout;
GLuint  vertex_object1;
GLuint  index_object1;

// ** Bind: Vertex=[0]  Index=[0]

glBindVertexArray( input_layout );
glBindBuffer( GL_ARRAY_BUFFER, vertex_object1 );

// ↓ ここでの bind は VAO input_layout に対して行う
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object1 );

// ** Bind: Vertex=[vertex_object1]  Index=[index_object1]

glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 12, 0 );
glEnableVertexAttribArray( 0 );

glBindVertexArray( 0 );

// ↑VAO の bind を解除したので index_object1 のバインドも消える↓

// ** Bind: Vertex=[vertex_object1]  Index=[0]

// VAO の変更によって GL_ELEMENT_ARRAY_BUFFER (index) の bind は
// 元に戻るが、GL_ARRAY_BUFFER (vertex) には影響がでない。

同じ glBindBuffer() 命令でも動作が異なっていることがわかるかと思います。

GL_ELEMENT_ARRAY_BUFFER のバインド情報
 ・VAO が 0 の時は Default の Bind 情報が保持される。
 ・VAO が割り当てられている場合は VAO に格納する。

例えば下記 (7) のようなコードがあった場合、(A) は描画やオブジェクトに影響を
与えませんが (B) は VAO (input_layout) の内容を置き換えています。

// GL3 -- (7)
glUseProgram( program );
glUniform4fv( location, 4, matrix );
glBindVertexArray( input_layout );

glBindBuffer( GL_ARRAY_BUFFER, vertex_object );        // -- (A)
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object ); // -- (B)

glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0 );

glBindVertexArray( 0 );

● Vertex Attribute Binding

(7) にあるように、描画時の (A) glBindBuffer( GL_ARRAY_BUFFER ) は直接
影響を与えません。

もし描画に使用する頂点バッファを置き換えたい場合は、Attribute 毎に
glVertexAttribPointer() を呼び直すことになります。
glVertexAttribPointer() に渡す TYPE などの情報が必要になりますし、
同じバッファを見ている Attribute の数だけ命令を実行しなければなりません。

OpenGL 4.3 では Vertex Buffer と Vertex Attribute を分けて管理できる
仕組みが追加されています。

glVertexAttribBinding( attrib_index, bind_index );
glBindVertexBuffer( bind_index, buffer_object, offset, stride );
glVertexAttribFormat( attrib_index, size, type, normalized, offset );

各 Attribute の頂点フォーマットは、これまでの
glVertexAttribPointer() 同様に glVertexAttribFormat() で定義します。

ただし各 Attribute が参照するバッファの情報は別で、bind_index を用いた
間接参照となります。
bind_index が指しているのは VertexBuffer の情報が入ったテーブルです。
実際のバッファの情報はこの VertexBuffer のテーブルの方に格納されます。

◎ GL3.x/4.x
・頂点 Attribute が持つ情報
    Type, Size, Offset, NormalizeFlag, VertexBuffer, Stride


◎ GL4.3/4.4 VertexAttribBinding
・頂点 Attribute が持つ情報
    Type, Size, Offset, NormalizeFlag, BindIndex (BindVertexBuffer を参照する)

・BindVertexBuffer のテーブルが持つ情報
    VertexBuffer, Stride

bind_index を通して、Attribute 毎に個別に持っていたバッファ情報を共有する
ことができます。

bind_index の値は glGetVertexAttribiv( GL_VERTEX_ATTRIB_BINDING ) で確認
することができます。
また実装上は Vertex Attribute の Table と Vertex Buffer の Table は
同一のもので、bind_index は自分自身への 2段階間接参照であることもわかります。

// GL3 --- (8)
GLuint input_layout= 0;

glGenVertexArrays( 1, &input_layout );
glBindVertexArray( input_layout );

glBindBuffer( GL_ARRAY_BUFFER, vertex_object );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object );

glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 24, (void*)0 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 24, (void*)12 );

glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );

glBindVertexArray( 0 );

(8) を VertexAttribBinding を使って置き換えると下記の通り。

// GL4.3/4.4 --- (9)
GLuint input_layout= 0;

glGenVertexArrays( 1, &input_layout );
glBindVertexArray( input_layout );

glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_object );

glBindVertexBuffer( 0, vertex_object, 0, 24 );
glVertexAttribBinding( 0, 0 );
glVertexAttribBinding( 1, 0 );

glVertexAttribFormat( 0, 3, GL_FLOAT, GL_FALSE,  0 );
glVertexAttribFormat( 1, 3, GL_FLOAT, GL_FALSE, 12 );

glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );

glBindVertexArray( 0 );

VertexAttribBinding を使うと、下記のように glBindVertexBuffer() 一つで
頂点が参照する Vertex Buffer を置き換えることができます。

// GL4.3/4.4 -- (10)
glBindVertexArray( input_layout );
glBindVertexBuffer( 0, vertex_object2, 0, 64 );
glBindVertexArray( 0 );

試してみると glBindVertexBuffer() は glBindBuffer( GL_ARRAY_BUFFER )
を経由しないし全く変更もしないことがわかります。
これで描画に glBindBuffer( GL_ARRAY_BUFFER ) は必要なくなりました。

関連エントリ
OpenGL ES 3.0 と Vertex Array Object