GLSLシェーダーを使って絵を描く

まず、OpenGLの簡単な説明から

OpenGLとは、クロスプラットフォームの3 次元グラフィックスAPIです。ハードウェアの機能を利用してリアルタイムにレンダリングすることができます。

詳細は省きますが、大まかな流れは以下のようになります。

まず、頂点シェーダで3次元座標を2次元座標に変換し、ラスター形式に変換、最後にフラグメントシェーダ(大雑把に言って色を付けたりぼかしたり)をやって完成となります。

このうち、頂点シェーダとフラグメントシェーダは、昔は固定で出来合いのものを使うしかなかったのですが、今はプログラマが定義する必要があります。

この「シェーダ」を記述するための言語の一つが、GLSLです。

OpenGLはC言語などで扱うため、実際のプログラムはCやJavaなどの言語とGLSLを組み合わせて使うことになります。

プログラマブルシェーダについて

プログラムを書いて実行モジュールを作るのは、こんな流れになると思います。

GLSLもコンパイルして実行する方式なので、基本的には同じです。

C言語のプログラムを実行するときと違う点として、

  • 出力されるのは実行ファイルではなくGPU依存の中間コードで、出力先はグラフィックボードのメモリなので、基本的に見ることはできない
  • プログラム実行時に毎回コンパイルされるので無駄な気もするが、グラフィックボードが変わっても同じコードが実行できるメリットがある。
  • VC++やGCCのようなコンパイラではなく、グラフィックボードのドライバに文字列を食わせることになる。

GLSLというプログラミング言語について

GLSL言語はこんな感じの言語です。

※フラグメントシェーダです。頂点シェーダは今回はやりません。

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;

vec3 getColor()
{
  return vec3(sin(gl_FragCoord.x / 16.0 + time) * 0.5 + 0.5);
}

void main(void)
{
  gl_FragColor = vec4(getColor(), 1.0);
}

ぱっと見、C言語っぽいですが、見慣れないものがあります。

  • 最初の#extensionは、まあそんなもんだと思ってください。
  • 変数宣言の最初にuniformとかがついている。
  • gl_FlagCoordやgl_FlagColorという変数をいきなり使っている。
  • vec2とかvec3という型が登場している。

まず、C言語の場合はmain関数が実行されますが、GLSL(というかシェーダ言語)の場合、1つ1つの画素に対してmainが実行されるようなものだと思ってください。

つまり。。。

for (gl_FlagCoord.y = 0; gl_FlagCoord.y < 描画領域の高さ; gl_FlagCoord.y++) {
  for (gl_FlagCoord.x = 0; gl_FlagCoord.x < 描画領域の幅; gl_FlagCoord.x++) {
    main();
  }
}

こんな感じで、画素の数だけmainが呼ばれます。

※実際は、パイプラインで並列処理されるのでこの説明は嘘です。

 

次に、uniformのついた変数宣言ですが、これはシェーダ外部から渡される変数の定義です。

timeには、経過時間(秒)が入ります。

 

gl_FlagCoordは、フラグメント(つまり画素)の座標が入ります。左下が原点です。

 

gl_FlagColorは、変数に代入することで、その画素に色が塗られると思ってください。

 

vec2やvec3は、float型が2個や3個集まった構造体のようなものです。

vec2は2次元座標、vec3は3次元座標、vec4は色(最後の1つはアルファチャンネル)に使われますが、用途が決まっているわけではありません。

動かしてみる

GLSLは、それ単独では動かすことはできず、絵を1枚出すまでのお膳立てが長くなってしまうので、ここでは説明しません。

ただ、動かすだけなら、WebGLというものがあるので、ブラウザ上で試すことができます。

 

http://glslsandbox.com/

このサイトでは、GLSLのフラグメントシェーダを使って、入力したプログラムが即座に反映されて絵が動きます。

(Chrome推奨)