はじめに
WebGL、できるようになるとかっこいいですが、なかなか理解しづらいですよね。
私もまだ初心者ですが、最初の入門としてこのような記事があればよかったなあと思いながら
備忘録も兼ねて概念の記事を書いてみることにしました。
ソースコードは出てきません。
WebGL
WebGLって何?
WebGLはブラウザからJavaScriptを使用してGPUに命令を出し、結果をCanvasエレメントに描画することができます。
GPUはGraphics Processing Unitの略で、画面上にグラフィックを描画する処理を行うユニットです。
GPUの持つ強い処理能力をブラウザで活用することで、ブラウザで3DCGのような高負荷のグラフィックを表示させちゃおうというのがWebGLです。
GLSL
GLSLは"OpenGL Shader Language"の略で、シェーダと呼ばれたりします。
名前にもOpenGLとあるように、OpenGL上で使われている言語です。
OpenGLとは、
クロノス・グループ が策定している、グラフィックスハードウェア向けの2次元/3次元コンピュータグラフィックスライブラリである。
というもので、
WebGLはこのモバイル版であるOpenGL ESから派生しているためGLSLを使用することができます。
WebGLでは、GLSLを使用して絵を描いていくことになります。
(GLSLはGPUの上で動くプログラムという認識で大丈夫だと思います。)
どうやって絵を描いていくの?
WebGLには
「丸を描いてください」「四角を描いてください」などの命令は存在しません。
基本的にできることは
- 頂点をどのように配置するのか
- どのように色を塗るのか
です。たとえば四角を描きたい場合、
- 4つの頂点を打つ
- 頂点で囲まれた中を塗りつぶす。
円を描きたい場合は
- 円の形に点を並べる
- 囲まれた中を塗りつぶす
といった具合です。打った点の数が少なければカクカクした円になりますね。
「頂点をどのように配置するのか」はVertex Shader、
「どのように色を塗るのか」はFragment Shaderで行います。
ちなみに、必ずVertex Shader -> Fragment Shaderの順で実行されます。
Vertex Shader
Vertex Shaderは頂点シェーダとも呼ばれます。
このシェーダーでは頂点の位置を動かす作業を行います。
「位置を動かす」のが仕事ということで、頂点の基本的な情報(言うなれば元データ)はJavaScriptで生成します。
(例)
三角形を作りたい!しかも回転させたいかも!
JavaScript: 点が3つ必要だな、点の座標はコレとコレとコレだな
↓
JavaScript: 座標データ送信!
↓
VertexShader: 座標データうけとった!え、三角形を回転させたい?じゃあこっちで座標動かして回転させとくで!
という感じです。
たとえば上記の三角形を回転する処理をVertex Shaderで行う場合、
三角形には3つの頂点が存在し、それぞれの頂点の位置を移動させる必要があります。
つまり頂点の移動処理を3回行う=頂点の個数分VertexShaderの処理が発生します。
Fragment Shader
Fragment Shaderはピクセルシェーダとも言われます。
このシェーダーでは受け取った頂点によって作られた図形を
どのように塗るのか決定します。
CGは最終的には必ず画面に表示されます。
画面にはピクセルが存在し、そのピクセル1つ1つに色がつくことで描画されるわけです。
このピクセル1つ1つに対し、何色になるべきかを計算するのがFragment Shaderの仕事です。
つまり描画範囲に存在するピクセルの個数回実行されるため、
描画範囲が広大であったり、画面が高画質であったりすると実行回数が増える=負荷が増える
ことになります。
流れをざっくりと図で
ざっくりですがこんな感じです。
本当はブレンディングなど、もっといろいろな処理が存在します。
なにやら壁がありますね、壁については次の項で解説します。
CPUとGPUの壁
CPUとGPUはユニットがそもそも違います。
メモリ空間も別のものになるため、相互でシームレスなやりとりをすることはできません。
GPUで計算を行うためには
CPU側で値をGPUで扱える形式に変換し、値を送り込む処理が必要になります。
処理はざっくりとこんな感じ。
その1: shaderの作成
GLSLを一生懸命に記述しても、それはCPU上ではただの文字列です。
GPUに送ることで初めて動作します。
したがってまずはWebGL APIを使用しGPU上で使用できる形にコンパイルする必要があります。
その2: programの作成
vertex shader, fragment shaderがそれぞれコンパイルできたら、次はその二つをつなげてprogramを作成します。
このprogramがGPUで動作するプログラムになると捉えてよいと思います。
このプログラムは1度作ればOKなので、最初の初期化として生成します。
その3: データの変換
頂点座標を入れた配列など、JavaScript上で取り扱っていた頂点毎のデータは、
GPUで使用できるように変換する必要があります。
これをVertex Buffer Object、略してVBOと言います。
頂点毎のデータとは座標のような、頂点ごとに異なる必要があるデータのことです。
例えば頂点毎に違う座標、違う色にしたい場合などは座標のVBO, 色のVBOというように、
属性毎にVBOを作成します。
その4: ロケーションの取得
あとは描画に必要なデータをprogram(GPUで動作するプログラム)へ送ってしまうだけです。
しかし、C言語などでいうポインタのような、送り込む先を指定しなければなりません。
これをロケーションと言います。
「vertex shaderのこの変数に座標の値を入れたいのかい?
じゃあこの変数の在り処(アドレス)を教えるね、その場所にデータを入れればここにちゃんとデータが入るよ」
というイメージです。
ちなみに図中のAttribute変数は
頂点毎に異なる変数のこと(頂点座標や色の指定など)
Uniform変数は
どの頂点、どのピクセルでも等しいグローバルな値(時間、マウス座標など)のことです。
その5: shaderへ値の送信
送り込む先のロケーション(アドレス)を教えてもらったので、これでようやく送信ができます。
送信が完了したら描画命令を発行しましょう。GPUでの処理が行われます。
番外編: ループ処理
もしアニメーションなどを行う際にJavaScript側のデータを更新するときは
- JavaScriptの元データ書き換え
- VBOの再生成
- programへデータ送信
を毎度やる必要があります。
pure webGLの処理
これらの処理を一生懸命に記述していく必要があります。
詳しい処理内容はDoxasさんのwgldをご覧ください。
ものすごく詳しく描いてあります。
WebGL 開発支援サイト wgld.org
おわりに
WebGLは初期セットアップを行うだけでも理解が難しく、大変苦しみました。
概念的なところを、とてもざっくりとしか書いていませんが、
私はこのような大きい流れから把握して少しずつ理解ができました。
もし同じような方がいれば、少しでも参考になればと思っています。