5. シェーダプログラム

3DS での 3D グラフィックスは、シェーダプログラムによってグラフィックスのパイプラインをカスタマイズし、様々なグラフィックスエフェクトを制御することができます。

3DS のシェーダプログラムには、頂点処理、ジオメトリ生成、フラグメント処理の 3 種類が存在します。

この内、頂点処理のシェーダプログラム(以降、頂点シェーダ)にはユーザーがプログラミングした独自のものを使用することができます。

ジオメトリ生成のシェーダプログラム(以降、ジオメトリシェーダ)は SDK で提供されており、それらのジオメトリシェーダと頂点シェーダをリンクして使用することができます。

フラグメント処理のシェーダプログラム(以降、フラグメントシェーダ)はプログラミングすることはできません。フラグメント処理は固定のパイプラインで実装されていますが、予約ユニフォームによる制御が可能です。以降、このフラグメント処理のパイプラインを"予約フラグメント処理"、シェーダプログラムを"予約フラグメントシェーダ"と記述します。

5.1. シェーダの作成

ユーザーが作成可能なシェーダプログラムは頂点シェーダのみです。頂点処理に関する一連の手続きは OpenGL ES 2.0 の仕様に従っていますが、いくつかの機能はサポートされていません。

シェーダプログラムは PICA グラフィックスコア独自のアセンブリ言語で記述します。アプリケーションがシェーダプログラムを使用するには、専用のアセンブラとリンカによって生成されたバイナリをロードし、アタッチしなければなりません。OpenGL ES 2.0 の仕様にある、glShaderSource() および glCompileShader() は実装されていません。

頂点シェーダの作成方法については、「8. 頂点シェーダ」および「頂点シェーダ リファレンスマニュアル」を参照してください。

5.2. シェーダのロード

5.1. シェーダの作成」でも述べたように、シェーダプログラムはバイナリデータをロードしてからアタッチしなければなりません。まず、glCreateShader() でシェーダオブジェクトを生成します。

コード 5-1. glCreateShader() の定義
GLuint glCreateShader(GLenum type);

ロードするシェーダプログラムの種別によって type に渡す値が変わります。予約フラグメントシェーダは固定実装のためロードする必要がありません。

表 5-1. シェーダオブジェクトの種別

type

生成されるオブジェクト

GL_VERTEX_SHADER

ユーザーが作成した頂点シェーダ用のオブジェクト

GL_GEOMETRY_SHADER_DMP

SDK が提供するジオメトリシェーダ用のオブジェクト

シェーダプログラムのバイナリをメモリ上にロードし、glShaderBinary() で GPU と関連付けます。

コード 5-2. glShaderBinary() の定義
void glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, 
                    const void* binary, GLint length);

shaders にはシェーダオブジェクトの配列、n には配列の要素数をそれぞれ渡します。アセンブラとリンカによって生成されたバイナリのみをロードすることができますので、binaryformat には GL_PLATFORM_BINARY_DMP を渡します。binary にはシェーダプログラムのバイナリをロードしたアドレス、length にはそのバイト数をそれぞれ渡します。

ロードされたシェーダプログラムはリンカに渡された順に配列に関連付けられます。配列の要素数および設定すべきシェーダオブジェクトの種別は、リンカが出力するマップファイルで確認することができます。詳しくは「頂点シェーダ リファレンスマニュアル」を参照してください。

5.3. シェーダのアタッチ

ロードされたシェーダプログラムをアプリケーションで使用するには、glCreateProgram() で生成したプログラムオブジェクトにシェーダプログラムをアタッチし、プログラムオブジェクトをリンクさせなければなりません。

コード 5-3. glCreateProgram() の定義
GLuint glCreateProgram(void);

OpenGL ES 2.0 とは異なり、プログラムオブジェクトはシェーダオブジェクトとは独立した 13 ビットの名前空間を持っています。そのため、同時に生成できるのは 8191 個までですが、生成済みのプログラムオブジェクトを glDeleteProgram() で破棄した場合には再度生成することができるようになります。

プログラムオブジェクトにシェーダプログラムをアタッチするには glAttachShader() を使用します。

コード 5-4. glAttachShader() の定義
void glAttachShader(GLuint program, GLuint shader);

program には glCreateProgram() の返り値を、shader にはシェーダオブジェクトを渡します。

頂点シェーダとジオメトリシェーダはロードしたバイナリからアタッチしましたが、ロードする必要のなかった予約フラグメントシェーダは、shaderGL_DMP_FRAGMENT_SHADER_DMP を渡してアタッチします。

1 つのプログラムオブジェクトには、頂点シェーダ、ジオメトリシェーダ、予約フラグメントシェーダをそれぞれ 1 つずつアタッチすることができます。つまり、ポイントシェーダ(ジオメトリシェーダ)をアタッチした後に続けてラインシェーダ(ジオメトリシェーダ)をアタッチした場合は、ラインシェーダだけが有効となります。

プログラムオブジェクトをリンクさせるには glLinkProgram() を使用します。

コード 5-5. glLinkProgram() の定義
void glLinkProgram(GLuint program);

複数のプログラムオブジェクトをリンクすることができます。ただし、リンクされたプログラムオブジェクトに glAttachShader() で別のシェーダプログラムをアタッチした場合は、再度 glLinkProgram() でリンクする必要があります。頂点シェーダとジオメトリシェーダで使用するユニフォームの合計数が 2048 を超えるプログラムオブジェクトのリンクは失敗します。

5.4. シェーダの使用

リンクされたシェーダプログラムを 3D 処理のパイプラインに適用するには glUseProgram() を使用します。

コード 5-6. glUseProgram() の定義
void glUseProgram(GLuint program);

この関数により、リンクされている複数のシェーダプログラムを切り替えて使用することができます。

OpenGL ではシェーダプログラムの正当性を glValidateProgram() で確認することができましたが、3DS では関数を呼び出しても何も行われません。

コード 5-7. glValidateProgram() の定義
void glValidateProgram(GLuint program);

5.5. シェーダのデタッチ

不要になったシェーダプログラムは glDetachShader() でデタッチすることができます。

コード 5-8. glDetachShader() の定義
void glDetachShader(GLuint program, GLuint shader);

5.6. シェーダの破棄

不要になったシェーダオブジェクトは glDeleteShader() で破棄することができます。

コード 5-9. glDeleteShader() の定義
void glDeleteShader(GLuint shader);

5.7. シェーダへの問い合わせ

有効・無効の判定やパラメータの取得など、シェーダに関連する情報はプログラムオブジェクトとシェーダオブジェクトに対する問い合わせで取得することができます。

5.7.1. 有効・無効の判定

プログラムオブジェクトやシェーダオブジェクトが有効であるかどうかを glIsProgram()glIsShader() で問い合わせることができます。

コード 5-10. glIsProgram()、glIsShader() の定義
GLboolean glIsProgram(GLuint program);
GLboolean glIsShader(GLuint shader);

これらの関数は、引数で渡されたプログラムオブジェクトやシェーダオブジェクトが有効であれば GL_TRUE を、そうでなければ GL_FALSE を返します。

5.7.2. アタッチされているシェーダオブジェクトの取得

プログラムオブジェクトにアタッチされているシェーダオブジェクトを glGetAttachedShaders() で取得することができます。

コード 5-11. glGetAttachedShaders() の定義
void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, 
                          GLuint* shaders);

program で指定されたプログラムオブジェクトにアタッチされているシェーダオブジェクトの一覧を、shaders に指定された配列に格納します。maxcount には shaders に指定した配列のサイズを指定してください。program に不正な値を指定した場合や maxcount に負の値を指定した場合は GL_INVALID_VALUE のエラーが生成されます。

count には格納したシェーダオブジェクトの数が格納されます。NULL を指定した場合は格納されませんが、アタッチされているシェーダオブジェクトの数は、後述する glGetProgramiv()GL_ATTACHED_SHADERS を渡して呼び出すことで取得することができます。

5.7.3. パラメータの取得

プログラムオブジェクトとシェーダオブジェクトのパラメータを glGetProgramiv()glGetShaderiv() で取得することができます。

コード 5-12. glGetProgramiv()、glGetShaderiv() の定義
void glGetProgramiv(GLuint program, GLenum pname, GLint* params);
void glGetShaderiv(GLuint shader, GLenum pname, GLint* params);

これらの関数は pname に指定されたパラメータ名に対応するパラメータの値を params に格納します。pname に不正な値が指定された場合は GL_INVALID_ENUM エラーが生成されます。program および shader に不正な値が指定された場合は GL_INVALID_VALUE エラーが生成されます。

glGetProgramiv()pname に指定可能なパラメータ名と params に格納されるパラメータを以下に示します。

表 5-2. glGetProgramiv() で指定可能なパラメータ名と格納される値

pname

params に格納される値

GL_DELETE_STATUS

プログラムオブジェクトが削除待ち状態ならば GL_TRUE が、そうでなければ GL_FALSE が格納されます。削除待ち状態へ遷移するのは、glUseProgram() で使用中のプログラムオブジェクトに対して glDeleteProgram() を呼び出したときです。

GL_LINK_STATUS

glLinkProgram() でプログラムオブジェクトのリンクが成功しているならば GL_TRUE が、そうでなければ GL_FALSE が格納されます。

GL_VALIDATE_STATUS

GL_LINK_STATUS を指定した場合と同じです。

GL_INFO_LOG_LENGTH

常に 0 が格納されます。

GL_ATTACHED_SHADERS

プログラムオブジェクトにアタッチされているシェーダオブジェクトの数が格納されます。

GL_ACTIVE_ATTRIBUTES

アクティブ状態の頂点属性の数が格納されます。

GL_ACTIVE_ATTRIBUTE_MAX_LENGTH

アクティブ状態の頂点属性のうち、最も長い名前の文字数が格納されます。文字数には終端文字(NULL)も含まれます。

GL_ACTIVE_UNIFORMS

アクティブ状態のユニフォームの数が格納されます。

GL_ACTIVE_UNIFORM_MAX_LENGTH

アクティブ状態のユニフォームのうち、最も長い名前の文字数が格納されます。文字数には終端文字(NULL)も含まれます。

glGetShaderiv()pname に指定可能なパラメータ名と params に格納されるパラメータを以下に示します。

表 5-3. glGetShaderiv() で指定可能なパラメータ名と格納される値

pname

params に格納される値

GL_SHADER_TYPE

シェーダの種別が格納されます。

GL_DELETE_STATUS

シェーダオブジェクトが削除待ち状態ならば GL_TRUE が、そうでなければ GL_FALSE が格納されます。削除待ち状態へ遷移するのは、プログラムオブジェクトにアタッチされているシェーダオブジェクトに対して glDeleteShader() を呼び出したときです。

GL_COMPILE_STATUS

常に GL_FALSE が格納されます。

GL_INFO_LOG_LENGTH

常に 0 が格納されます。

GL_SHADER_SOURCE_LENGTH

常に 0 が格納されます。