GLSL

GLSL (OpenGL Shading Language) はGLslangとしても知られ、C言語の構文をベースとした高レベルシェーディング言語である。これはアセンブリ言語やハードウェアに依存した言語を使わないで、アプリケーションソフトウェア開発者がグラフィックスパイプラインを直接制御できるようにOpenGL ARB (Architecture Review Board) で策定された。 Direct3D 7までの時代、すなわち1990年代までのリアルタイム3Dコンピューターグラフィックスは、OpenGLやDirect3DといったAPIを通して、グラフィックスカード上のチップ（GPU）にあらかじめ用意された固定のレンダリングパイプライン上で、固定機能のシェーダー（頂点トランスフォームや陰影計算を専門に担当するユニット）を組み合わせることで実現されていた。Direct3D 8が登場した2000年以降は、GPUの進化・性能向上に伴い、新機能はハードウェア実装による固定機能ではなく、アプリケーション開発者がソフトウェアプログラム（プログラマブルシェーダー）によって頂点レベル・フラグメントレベル（ピクセルレベル）での制御・カスタマイズを行ない、レンダリングパイプライン内での柔軟性や表現力を増すことができる形で追加されることが多くなっている。 元々、このプログラマブルシェーディング機能は複雑で直感的でないアセンブリ言語で書かれたシェーダーを使わないと実現できなかった。OpenGL ARBは、OpenGLをグラフィックス産業の歴史の中でオープンスタンダードなものにしていく中で、グラフィックス処理を行うプログラミングをより直感的・効率的にできる方法として、OpenGL Shading Languageを作り出した。 OpenGL Shading Languageは2003年に発表されたOpenGL 1.5の拡張機能として導入されたが、OpenGL ARBはOpenGL 2.0にGLSLを含めることを正式に決定した。OpenGL 2.0は1992年に発表されたOpenGL 1.0から数えて初のメジャーバージョンアップである。 初期のOpenGLプログラマブルシェーダーは、頂点単位のトランスフォームや陰影計算を行なうバーテックスシェーダー (vertex shader) と、フラグメント（ピクセル）単位の陰影計算を行なうフラグメントシェーダー (fragment shader) のみが利用可能であった。その後、プリミティブの増減や変更などを実行できるジオメトリシェーダー (geometry shader) がOpenGL 3.2/GLSL 1.5にて標準化された。またOpenGL 4.0で固定機能シェーダーであるテッセレーションステージが追加されるに伴い、テッセレーション・コントロールシェーダー (tessellation control shader) と、テッセレーション・エバリュエーションシェーダー (tessellation evaluation shader)、これら2つのプログラマブルなシェーダーがGLSLの仕様に追加された。フラグメントシェーダーもサンプルレベルでの制御が可能となった。 なお、OpenGLと同様の3DグラフィックスAPIであるDirectX（Direct3D）およびそのシェーディング言語であるHLSLにはバージョン11以降、GPUにおける汎用的なコンピューティング（GPGPU）を可能とするDirectX Compute Shader（DirectCompute）が追加されているが、OpenGLおよびGLSLのバージョン4.0時点ではこれに相当するシェーダーは含まれていなかった。しかし、バージョン4.3においてOpenGLコンピュートシェーダー (compute shader) として同等機能が導入されることになった。なお、コンピュートシェーダーの導入以前からOpenGLの管轄を行なっているクロノスが同様にオープン仕様として策定している、GPUを汎用コンピューティングに用いることのできるAPIとしてOpenCLが存在するが、こちらはCPUやGPU等あらゆる計算資源を計算に用いることのできる異種計算資源混在（ヘテロジニアス）環境向けのAPIであり、グラフィックスパイプラインとの連携を主目的としたコンピュートシェーダーとは得意分野が若干異なる。 GLSLを使うメリットとして、 レンダリングアルゴリズムの柔軟なカスタマイズや再利用性が増すことで、従来のハードウェア固定機能にとらわれない、柔軟でユニークかつ高品質なリアルタイム3DCGシーンの構築が可能となる。 MacintoshやWindows、Linuxを含む複数のOS間での互換性を確保できる。 アセンブリ言語を用いるよりもコードの再利用性やメンテナンス性が増す。 OpenGL Shading LanguageをサポートするどんなハードウェアベンダーのGPU上でも動作するシェーダーを書くことができる能力を持つ。 それぞれのハードウェアベンダーはデバイスドライバー内にGLSLコンパイラを含めることができるので、そのGPUのアーキテクチャに最適化されたコードを生成することができる。 などが挙げられる。従来の固定機能シェーダーに対するデメリットとしては、 シェーダーのコンパイルおよびアタッチなど、レンダリングのための準備作業が増える。 OpenGL APIの他に、GLSLの学習コストがかかる。 GPU特性やハードウェア仕様を把握してGLSLコードを記述する必要があり、CPUと比較してチューニングが難しい。 などが挙げられる。 なお、GLSLの派生規格として、組み込み環境向けのOpenGL ES用のシェーダー言語「GLSL ES」が存在する。これはESSLと呼ばれることもある。 Webブラウザ向けのOpenGL ES派生規格としてWebGLが存在するが、WebGLでもGLSLが使用される。 クロノスが策定しているローレベルグラフィックスAPIであるVulkanは、シェーダープログラムの中間表現SPIR-Vを入力として受け付けるが、SPIR-Vを出力するオフラインシェーダーコンパイラglslangValidatorにおいて最初にサポートされた上位レベルシェーディング言語はGLSLである。のちにSPIR-VはOpenGL 4.6にも導入された。 OpenGL Shading Language 1.50の仕様では64の基本データ型を定義している。いくつかはC言語内で使われていたものとまったく同じものであるが、一方他のものはグラフィックス処理に特化している。 例えば以下のような型が定義されている。 void 値を返さない関数に用いる bool 条件型。値はtrueかfalseのどちらかをとる int 符号付32bit整数 float 単精度浮動小数点数 vec2 2要素を持つ単精度浮動小数点数ベクトル vec3 3要素を持つ単精度浮動小数点数ベクトル vec4 4要素を持つ単精度浮動小数点数ベクトル bvec2 2要素を持つ論理型ベクトル bvec3 3要素を持つ論理型ベクトル bvec4 4要素を持つ論理型ベクトル ivec2 2要素を持つ符号付32bit整数ベクトル ivec3 3要素を持つ符号付32bit整数ベクトル ivec4 4要素を持つ符号付32bit整数ベクトル mat2 2x2要素を持つ単精度浮動小数点数行列 mat3 3x3要素を持つ単精度浮動小数点数行列 mat4 4x4要素を持つ単精度浮動小数点数行列 sampler1D 1次元のテクスチャにアクセスするためのハンドル sampler2D 2次元のテクスチャにアクセスするためのハンドル sampler3D 3次元のテクスチャにアクセスするためのハンドル samplerCube キューブマップテクスチャにアクセスするためのハンドル sampler1DShadow 1次元のデプステクスチャにアクセスするためのハンドル sampler2DShadow 2次元のデプステクスチャにアクセスするためのハンドル なお、符号なし32bit整数のサポートはOpenGL 3.0 (GLSL 1.3) で標準化され、また科学計算などの分野で必要とされることの多い倍精度浮動小数点数のサポートはOpenGL 4.0 (GLSL 4.0) で標準化されている。 OpenGL Shanding LanguageはホストプログラムにC言語がよく使われていることを背景にして、それによく似た多くの演算子、加えてベクトル演算に特化した特殊な演算子（Tupleを返すSwizzle演算など）も提供している。このことにより、シェーダー開発者はシェーダーを柔軟にかつ効率よく書くことができる。GLSLはポインタを除くCやC++での演算子を含んでいる。 GLSLは、C言語に見られるif/else if/else、for、while、do-while、break、continueなどの繰り返しや分岐といった制御文もサポートしている。 いくつかの組み込み関数（ビルトイン関数）のほか、ユーザー定義関数もサポートしている。GPUベンダーは、必要に応じて組み込み関数の実装をハードウェアレベルで最適化することもできる。組み込み関数の多くはexp()やabs()のようなC言語の標準ライブラリで見られるものとよく似ているが、一方でsmoothstep()やtexture2D()のようなグラフィックスプログラミングに特化しているようなものもある。 GLSLコード内における変数の宣言・使用方法は、C言語でのそれに似ている。 変数の修飾子は4つある（以下バーテックスシェーダー、ジオメトリシェーダー、フラグメントシェーダーをそれぞれVS, GS, FSと示す）。 const 変化しない。 uniform 全てのシェーダーで使用可能なグローバルな読み出し専用変数。 in 入力変数。VSではOpenGLから渡される頂点情報セットを表す（旧attribute）。GSではVSによって計算された頂点情報セット、FSではVSまたはGSによって計算された頂点情報が補間された値を表す（旧varying）。 out 出力変数。 VSではGSまたはFSへ（旧varying）、GSではFSへ渡す頂点情報セットを表す。FSでは最終的にピクセル単位で出力する色情報を表す。 inout 入出力変数。 OpenGL 4.3 (GLSL 4.3) 以降、OpenGL ES 3.2 (GLSL ES 3.2) 以降、およびVulkan 1.0以降で、以下の6種類のシェーダーステージに対応している。 頂点シェーダー (vertex shader) テッセレーション制御シェーダー (tessellation control shader) : GLSL 4.0 / GLSL ES 3.2で対応 テッセレーション評価シェーダー (tessellation evaluation shader) : GLSL 4.0 / GLSL ES 3.2で対応 ジオメトリシェーダー (geometry shader) : GLSL 1.5 / GLSL ES 3.2で対応 フラグメントシェーダー (fragment shader) コンピュートシェーダー (compute shader) : GLSL 4.3 / GLSL ES 3.1で対応 GLSLシェーダープログラムは単体のアプリケーションではない。シェーダーの実行にはOpenGL APIを利用するホストアプリケーションが必要である。ホストアプリケーションを記述する言語には、OpenGL APIをサポートする第一級言語であるC言語およびC++、あるいはWebGLをサポートするJavaScriptなどがよく利用される。 従来方式では、GLSLシェーダーはOpenGL API関数を通じて、ハードウェアベンダーの実装したデバイスドライバー上のオンラインコンパイラによって実行時にコンパイルされる。GLSLシェーダープログラムのソースコードはホストプログラム上の文字列リテラルとして記述されたり、アプリケーション実行時に外部テキストファイルなどから読み込まれたりしたものがメモリ上の文字列データとして実体化されるが、オフラインの事前コンパイル方式ではなく、あくまでドライバーにはソースコード文字列の形式で送られ、ドライバーが実行時にシェーダーのソースコードをコンパイルして「シェーダープログラムオブジェクト」を生成する。具体的な手順として、アプリケーションはまずglCreateShader()で各シェーダーステージのシェーダーオブジェクトを生成し、そのシェーダーオブジェクトに対してGLSLソースコード文字列をglShaderSource()で設定した後、glCompileShader()によってシェーダーをコンパイルする。その後、glCreateProgram()で生成したプログラムオブジェクトに対して、glAttachShader()で前述のコンパイル済みシェーダーオブジェクトを関連付け、glLinkProgram()でリンクすることで、ようやく一連のプログラマブルシェーダーパイプラインが完成する。シェーダープログラムを利用して描画するには、描画命令を発行する前にglUseProgram()で現在のOpenGLコンテキストにプログラムオブジェクトをバインドしておく必要がある。 シェーダープログラムのコンパイルやリンクは時間のかかる処理であり、アプリケーション初期化のボトルネックとなりうるため、直近のソースコード文字列に対応するコンパイル結果はドライバー側でキャッシュされる実装になっていることも多い。なお、OpenGL 4.1で標準化されたGL_ARB_get_program_binaryにより、コンパイル済みバイナリのシリアライズ・逆シリアライズが拡張としてサポートされるようになったが、バイナリがベンダー間で互換性のある中間形式であるかどうかは保証されない。OpenGL 4.5で標準化されたGL_ARB_parallel_shader_compileにより、マルチスレッドを利用したシェーダーの並列コンパイルが拡張としてサポートされるようになった。デバイスドライバーがGLSLコンパイラを内蔵していることから、ドライバーによってシェーダープログラムのコンパイル結果や実行結果が異なる可能性があるなどの品質問題も抱えている。 OpenGL 4.6では中間表現SPIR-Vがサポートされるようになり、オフラインコンパイルが可能になったことから、OpenGLでプログラマブルシェーダーを利用するために必ずしもGLSLを利用する必要はなくなった。なお、Direct3Dのシェーディング言語であるHLSLはリリース当初から、コンパイル結果はベンダー非依存のバイトコードで出力され、またコンパイル済みバイナリの読み込みもサポートしていた。 これは固定機能パイプラインと同様に入力頂点を変換する。 void main(void) { gl_Position = ftransform(); } ftransform()はGLSL 1.40とGLSL ES 1.0からはサポートされない。代わりに、プログラマは新しいOpenGL 3.1標準に従いモデルビュー行列と投影行列を明示的に指定する必要がある。 #version 140 uniform mat4 projectionMatrix; uniform mat4 modelviewMatrix; in vec3 position; void main(void) { gl_Position = projectionMatrix * modelviewMatrix * vec4(position, 1.0); } layout(triangles) in; layout(triangle_strip, max_vertices = 3) out; in Input { vec4 Position; } VSout[3]; void main(void) { for(int i = 0; i < 3; i++) { gl_Position = VSout[i].Position; EmitVertex(); } EndPrimitive(); } void main(void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red. } GLSLはOpenGL仕様とともにバージョンアップされている。OpenGL 3.2までは、対応するGLSLのバージョン番号はOpenGL本体のバージョン番号と無関係にナンバリングされていたが、OpenGL 3.3以降は同一の番号が割り当てられるようになった。 GLSL ESもOpenGL ES仕様とともに更新されている。 GLSLシェーダーは単独で動作するプログラムではなく、シェーダープログラムをGPU上で走らせるためには、まずC/C++などで書かれたホストアプリケーションに入力する必要がある。具体的にはOpenGL APIを使ってシェーダープログラムをコンパイル・リンクしたのち、OpenGLレンダリングコンテキストにプログラムオブジェクトをバインドしてからプリミティブのレンダリングを実行する必要があるが、それらの一連の処理を簡略化するためにいくつかのGLSL開発者・デザイナー用ツール（オーサリングツール）が存在する。 RenderMonkey - ATIによって開発された。DirectXシェーダーと同様にGLSLシェーダーを作成、コンパイル、デバッグできるインタフェースを提供している。Microsoft Windows上でのみ実行できる。GPUOpen（英語版）に移管されているが、開発およびサポートは終了している。 GLSLEditorSample - macOS上でのみ実行できるCocoaアプリケーション。シェーダーの作成とコンパイルはできるがデバッガ機能は組み込まれていない。 Lumina - 新しいGLSL開発ツール。プラットフォーム独立でインタフェースにQtを使っている。 Blender - GPLライセンスのモデリングおよびアニメーション作成パッケージで、バージョン2.4.1で内蔵しているゲームエンジンがGLSLをサポートするようになった。 Mozilla Firefox - 開発者向けツールとして、WebGL用のバーテックスシェーダー・フラグメントシェーダーを参照・編集できる"シェーダーエディター"が実装されている。