OpenGL, OpenGLES, WebGLなどに採用されているシェーダー言語。Vertex Shader, Fragment Shaderなど複数のシェーダーをパイプラインを通じて実行させ、最終出力を得る。OpenGLES側等で、glCreateShaderでシェーダー登録用の番号(シェーダーID)を作成し、その番号と実際のシェーダーソーステキストを glShaderSourceで指定することで、OpenGLESに読み込まれ、glCompileShaderを使って、内部の実行可能なコードを作っておくことで、いつでもこの、シェーダーIDを使ってシェーダーの利用設定ができる。通常はアプリの初期処理ですべて読み込んでおく場合が多いだろう。
このシェーダー登録の上限はメモリの許す限りであり、ゲームなどのシェーダーを多用するアプリでは、2,000個近くのシェーダーが登録されることもある。
その後、各種シェーダーの組み合わせをプログラムIDという名前で登録して、そのプログラムを利用という指定方法になる。glCreateProgramによって、プログラムIDを作成し、glAttachShaderで、そのプログラムIDと利用するシェーダーIDを登録する。この関数は登録するシェーダー数だけ実行される(Vertex とFragmentだけなら2回)。その後、glLinkProgramで各種シェーダープログラムが接続されこのプログラムIDが動作できる。(通常ここでも内部でコンパイルされるので、コンパイルエラーを吐く、エラーの取得方法は別ページを参照の事)またこのプログラム作成も上限はメモリの許す限りであるため、初期処理など最初に作成しておくのが良いだろう。(コンパイルには多少のオーバーヘッドが予想されるため)
実際に利用する場合には、このプログラムIDを利用する為 glUseProgram という関数を利用することで、この登録されたプログラムIDが描画の段階で動作する。通常は1つのオブジェクトに1つのプログラムという使い方も少なくなく。その場合にはこのプログラムIDをオブジェクト描画ごとに切り替えることになる。
シェーダーのコンパイルは、プログラム実行時にコンパイルされるため、ソース中でソースを指定してコンパイルを行うように指示をする。
順番 | もどり値 | OpenGLの関数 | 概要 |
1 | GLuint (shader番号) | glCreateShader(GLenum type) | バーテックスシェーダとフラグメントシェーダのシェーダオブジェクトを作成する。typeは、GL_VERTEX_SHADER と、GL_FRAGMENT_SHADERなどが選択できる |
2 | void | glShaderSource(GLuint shader, GLsizei count, const GLchar ** shaderSource, const GLint * length) | 作成したそれぞれのシェーダオブジェクトに対してソースプログラムを読み込む。lengthはNULLでもOK |
3 | void | glCompileShader(GLuint shader) | 読み込んだソースプログラムをコンパイルします |
4 | void | glDeleteShader(GLuint shader) | シェーダオブジェクトを削除する |
順番 | もどり値 | OpenGLの関数 | 概要 |
1 | void | glGetShaderiv(GLuint shader, GLenum type, GLint * status) | コンパイラの結果などを戻す関数。type には、GL_COMPILE_STATUS, GL_LINK_STATUSなどを設定する。statusに結果が戻ってくる |
順番 | もどり値 | OpenGLの関数 | 概要 |
1 | GLuint | glCreateProgram() | プログラムオブジェクトを作成する |
2 | glAttachShader() | プログラムオブジェクトに対してシェーダオブジェクトを登録する | |
3 | void | glBindAttribLocation(GLuint program, GLuint index, const GLchar * name) | ※LinkProgramより前に行う必要がある |
4 | void | glLinkProgram(GLuint program) | シェーダプログラムをリンクします |
5 | glUseProgram() | シェーダプログラムを適用する | |
6 | void | glDetachShader(GLuint program, GLuint shader) | プログラムオブジェクトからシェーダオブジェクトのリンクを解除する。※DeleteShaderの前に行う事 |
OpenGL ES | GLSL ES | 追加された機能 |
1.0 | 未対応 | |
1.1 | 未対応 | バッファオブジェクト、自動ミップマップ生成、拡張テクスチャ処理、頂点スキニング機能、ユーザー定義クリッププレーン、拡張ポイントスプライト、ポイントスプライト配列、静的・動的状態クエリー、テクスチャ描画、新しいコア追加、プロファイル拡張 |
2.0 | 1.0 | プログラマブルシェーダー対応(Vertex, Fragmentの2つ) |
3.0 | 3.0 | マルチレンダーターゲット、マルチサンプルアンチエイリアス(MSAA)、Uniform Block、Transform Feedbackなど多数 |
3.1 | 3.1 | コンピュートシェーダーなど |
3.2 | 3.2 | ジオメトリシェーダー、テッセレーションシェーダー、テクスチャ圧縮技術ASTC、など多数 |
シェーダーコードは基本的にC言語と同じ記述方法で記載する。よって基本は main 関数にそのコードを記載する形となり、通常はグローバル変数や、シェーダー変数などは main より前に記述されることが多い。独自関数や引数、戻り値などの基本仕様はすべてC言語と同じとなる。
GLSL ESのバージョンを指定するには、下記の行をシェーダーソースの先頭に記入する。
バージョン | 指定方法 |
1.0 | <バージョン指定無し> |
3.0 | #version 300 es |
extension指定されている機能を利用したい場合には、#extension を利用できる。
<例>
#extension GL_ARB_explicit_attrib_location : enable
概ねC言語の記述通りではあるが、GLSLシェーダー独特の修飾子が存在する。
修飾子 | 概要 |
in, out, inout | C++で言う所の参照渡し。他の関数で定義変数を、他の関数から簡単に修正できる。ある意味危険ではあるが、高速化には役に立つ。in は更新のみ、out は参照のみ、inout は読み書きできる変数として指定できる。 |
highp, mediump, lowp | 精度修飾子。C言語のように short, long, long long などの表現ではなく、highp int のように整数型をより高精度にする。というような表記になる。詳細は下記を参照 |
varying | Vertex Shaderから、Fragment Shaderに値を引き渡したい場合に、両方のコードに同じ型で同じ名前の変数を記述し、varying修飾子を記述することで実現できる。※ただしGLES3.0以降では削除され、in out が代行する |
uniform | OpenGLESプログラムからシェーダーに値を渡したい場合に利用する。通常は sampler2Dなどのビルトイン修飾詞とセットで指定をし、Vertex Shader、Fragment Shaderの両方のソースから利用できる。基本的にはどのような型にも利用できる汎用修飾子。※GLES3.0以降からは uniform ブロックにも対応 |
attribute | 上記 uniform と同じOpenGLESプログラムからシェーダーに値を渡したい場合に利用する。gl_Vertex、gl_Normal、gl_Position、gl_PointSizeなどの頂点に関するビルトイン変数を利用する場合に指定し、Vertex Shaderからのみ利用することができる。基本的には頂点情報だけをやり取りするための専用修飾子。※ただしGLES3.0以降では削除され、in out が代行する |
centroid | MSAAのポリゴンの外がピクセルの中心になる問題に対して、必ずポリゴン内で補間するように補正する変数に対して指定する。※GLES3.0以降で利用できる修飾子 |
smooth, flat | 値の受け渡しの際の補間の方法を指定可能。指定が無い場合はsmooth。int型の場合には flatのみ指定可能。※GLES3.0以降で利用できる修飾子 |
layout | 属性インデックスをGLSL側で指定可能。他にも、shared : 複数のプログラムでのレイアウトの一貫性を保証、packed : コンパイラによって変数配置の変更削除などの最適化を許可、std140 : std140のレイアウトに設定、row_major : 行列を行優先、column_major : 行列を列優先などの変数の振る舞いを設定可能。※GLES3.0以降で利用できる修飾子 |
これらビルトイン変数は原則、3.0の仕様となる。gl_FragColor、gl_Color、gl_SecondaryColor、gl_Normal、gl_Vertex、gl_MultiTexCoord、gl_FogCoord、attribute、varying、gl_FragColor, shadow?D, texture?Dなど、多くのビルトイン関数が削除された。
入出力 | 変数名 | 型 | 概要 |
入力 | gl_VertexID | int | 頂点番号が設定される |
入力 | gl_InstanceID | int | インスタンス作成の場合にインスタンス番号が設定される |
出力 | gl_Position | vec4 | クリッピングスペース上における座標。通常の座標を求めたい場合は、(x/w, y/w, z/w)で得られる。 |
出力 | gl_PointSize | float | 点のサイズ |
入出力 | 変数名 | 型 | 概要 |
入力 | gl_FrontFacing | bool | ポリゴンの表裏のブール値 |
入力 | gl_FragCoord | vec4 | ウィンドウ座標 (x,y,z, 1/w) |
入力 | gl_PointCoord | vec2 | ポイントスプライト時の2次元座標 |
入力 | gl_HelperInvocation | bool | 3.0では使えない。4,5以上 |
出力 | gl_FragDepth | float | デプス値を設定 |
上記のようにC言語とは精度表現が違う。精度修飾子は当然ながら1つの変数に1つだけつけることが出来る。
修飾子付きの型 | 提供される精度 | C言語で近い型 |
--- | --- | double double(精度:2の112乗) |
highp float | 範囲:2^-62〜2^62 精度:2^16 | double(精度2の52乗) |
mediump float | 範囲:2^-14〜2^14 精度:2^10 | float(精度2の23乗) |
lowp float | 範囲:2^-8〜2^8 | float(精度2の23乗) |
highp int | ±2^16 | long(±2^31) |
mediump int | ±2^10 | short(±2^15) |
lowp int | ±2^8 | short(±2^15) |
vertex Shaderコードは、lowp、mediump、そしてhighpのいずれを使用してコンパイルとリンクを行っても、エラー無しである必要があり、また Fragment Shaderコードは、lowpとmediumpを使用してコンパイルしてもエラー無しである必要がある
precision特別修飾子を利用することで、精度修飾子の初期値を設定できる。
例:precision highp float
これで、それ以降の float は指定がなければ、すべて highp 精度修飾子が適用される。
GLSLにおける定数はC言語で利用されている、static や const などの修飾詞は使えないため、通常の変数として定義する。また右側にも型を指定する必要がある。
<例>
float myArray[4] = float[]( 1.0f,1.0f,1.0f,1.0f );
いわゆる定義のキャストは、C言語とは表記が違う。
<C言語:値の前に括弧で型を宣言>
int my_int = (int) my_float;
<GLSL:型を記載し引数の形で値を指定>
int my_int = int ( my_float );
プリミティブの種類 | プリミティブ識別定数 | 入力頂点数 | 概要 |
点 | GL_POINTS | 1 | 点のサイズが設定できるため、ポイントスプライトが利用できる(点のサイズを大きくしてそこにテクスチャーを貼ることで、常に画面側に表示されるビルボード的な表現方法) |
ライン(エッジ) | GL_LINES | 2 | |
三角形ポリゴン | GL_TRIANGLES | 3 | ポリゴンの基本形態。面になるため実際の立体物にはこのプリミティブでの表現が中心になる。 |
シェーダープログラムとメインプログラムとの接続には、各変数タイプ毎にOpenGLES関数が提供されている。基本はシェーダー内の変数IDを取得し、そのIDを利用して値を操作する形となる。こここでの、programID とは、glUseProgram関数で指定されたものを指す。
関数名 | 概要 |
glGetUniformLocation(programID, <uniform変数名>) | uniformで定義されている変数のIDを取得する。 |
glGetAttribLocation(programID, <attribute変数名>) | attributeで定義されている変数のIDを取得する。 |
glEnableVertexAttribArray | 指定した変数IDの変数に対して頂点座標の配列属性を有効にする |
glVertexAttribPointer | 上の関数で座標属性を有効化したあと、その変数IDに各種頂点に関する設定と、頂点データのポインタを指定することで変数の内容を更新します |
glVertexAttrib | 頂点配列や頂点バッファを使用せずに頂点データを渡す場合はこちらを使用 |
glUniformXXXXX | 上のattribute変数用に対して、uniform変数への操作にはこちらを利用する。XXXXXの部分は、変数の型に合わせて各種準備されている。例:glUniformMatrix4fvなど |
シェーダーソースをUTF-8で保存する場合は、BOMコードをつけないようにし、改行コードはUnixのLF(0x0A)のファイル形式で保存すること。Windows形式の改行コード 0x0D, 0x0Aなどの場合、ファイルの先頭から Syntax Errorになる場合がある。
シェーダーで利用できる変数の方には、最小管理単位として浮動小数点型と整数型の2種類しかない。またそれら最小管理単位を複数まとめた集合型(配列でも操作できるので配列型とも認識できる。また代表的用語としてベクター型とも言う)という指定があり、代表的なものには vector型やmatrix型などがある。
要素の型 | 言語予約型 | 各要素名 | 主な使い方 |
float | vec2 | ||
vec3 | |||
vec4 | x, y, z, w | 命名セット xyz型。主に座標や法線を表すベクトルに表現する場合 | |
r, g, b, a | 命名セット rgb型。色を表すベクトルにアクセスする場合に使用 | ||
s, t, p, q | 命名セット stpq型。テクスチャ座標を表すベクトルにアクセスする場合に使用 |
集合型(例:vec4など)は、通常のC言語と大きく違い、各要素をまとめてアクセスする表記がある。ただし省略指定は上記の命名セット毎でしか利用できない。
vec4.xy ... vec2型 として x y の2つの要素として取得出来る。 vec4.rgb ... vec3型として、r g b の3つの要素として取得出来る。
いわゆるC言語での型のキャストに似ているが、キャストを明示しなくても、要素の組み合わせで自動的に新しい型として利用できる。応用としては拡張も可能。
vec4 = vec4( vec2, vec2 ) vec4 = vec4( vec3, 1.0 ) vec4 = vec4( vec4.xy, 1.0, vec4.w )
集合型は配列番号によってもアクセスができる。
vec4.x == vec4[0] は同じ場所を指す。 vec4.b == vec4[2] は同じ場所を指す。
OpenGLES関数では、関数の末尾に特定の文字を付与することでベクトル要素を明確に指定している。
修飾文字 | 内容 |
関数の末尾に「i」 | integer 型を表す |
関数の末尾に「v」 | vector 型を表す |
attributeなどの修飾子の廃止に合わせて、ビルドイン変数も削除された。例えば fragment シェーダーでは、gl_FragColorなどの出力先用のビルトイン変数が存在していたが、削除され、out 指定されたものが、その対象になるようになった。
<旧>
main () { gl_FragColor = vColor; }
<新>
out vec4 ouputColor; main () { outputColor = vColor; }
シェーダーコード内で、テクスチャー情報を受け取るためには下記の定義が必要になる。
uniform sampler2D texture;
NVIDIAが提供する Cg Toolkit をインストールすると、cgc コマンドがインストールされ、下記のように利用することで、cg shader のソースを、GLSL のシェーダーソースにコンバートできる。
# cgc -profile glslv -profileopts version=100 -o myshader.vsh myshader.cg
注意点は OpenGL ES 2.0の、GLSL 1.0 では、コンバートされたGLSLソースの先頭に記述される、#version 指定がエラーとなるので、version は 100以外を利用しないように注意。profile には、vertexなら glslv、fragmenなら glslf、geometryなら glslg となるようだ。
またコンバート結果で attribute 指定に ivec4 を指定する場合があるが、GLSL では attributeで int型が使えないためエラーになる。よってattribute ではなく、uniform にする必要がある。他にもビルトイン変数である、gl_Color や、gl_Vertex , gl_TexCoord などが、定義されないため varying で定義が必要になる場合がある。
<profileオプションの詳細(共通)>
指定できるprofileopts | 解説 |
version=<val> | ターゲットのバージョンを指定 |
userTexCoord | gl_TexCoordの代わりにユーザー定義を利用 |
ATI_draw_buffers | use ATI_draw_buffers extension for MRT |
EXT_gpu_shader4 | use EXT_gpu_shader4 extension where useful |
ARB_draw_instanced | use ARB_draw_instanced instead of EXT_draw_instanced |
ARB_uniform_buffer_object | use ARB_uniform_buffer_object extension |
Tegra K1 などのチップでは、precision 指定を先頭に入れておかないとエラーになる場合がある。
precision highp float;
GPUが、GL_EXT_Cg_shader に対応していればそのまま利用できる。
変数名などが違っていて、glGetUniformLocationの戻り値が、-1になっておりそれに気が付かず、glUniform1fなどの変数アクセスをしても glerror にならない。これを利用して、シェーダーのソースの共通化の工夫が出来る。
glGetUniformLocation などのindex取得 = linkProgramの後。programID取得後。 glUniform1f などのアクセス = glUseProgramの後
OpenGLES3.0の一部の実装では下記のハーフフロートオプションが利用できる。対応しているGPUかどうかは、GL Specを確認のこと。
主な使い方はCPU側では、型の指定に GL_FLOAT の代わりに、GL_HALF_FLOAT_OES を指定することで、利用できる。シェーダーでは、#version 300 es の次の行あたりに、#extention