グラデーションの実装
- 頂点シェーダー
- フラグメントシェーダー
今回はグラデーションの実装について考えてみる。
前回まではgl_FragColor
に固定の値を格納していた。つまりすべてのフラグメントシェーダーで同じ値を実行していたので、単色のオブジェクトがレンダリングされていた。
一方でグラデーションを表現するためには、ピクセルごとに色を変える必要があるので、gl_FragColor
に格納する値を変える必要がある。その値をどのように用意するのか。まずはgl_FragCoord
について考えてみる。
gl_FragCoord
はGLSLの組み込み変数であり、対象となるピクセルの座標が入っている。gl_FragCoord
はvec4型
ではあるが、gl_FragCoord.xy
とすれば、x座標とy座標が取得できる。たとえば一辺が500pxの正方形だとすると、左下が(0, 0)、右上が(500, 500)の値となる。この時左下が原点であることに注意する。
では早速gl_FragCoord
を使って、以下のようにfragmentShader
を実装してみる。黒から赤へのグラデーションが何回か繰り返されているのが確認できる。
先にfract
について簡単に説明しておくと、fract
はGLSLの組み込み関数で、x - floor(x)
の計算をしてくれる関数である。「The Book of Shaders: fract」を読むとイメージがつきやすい。ようは値を渡すと少数部分を返してくれる関数であり、たとえばfract(1.5)
は0.5
、fract(2.3)
は0.3
となる。
// gl_FragCoord.x にはピクセルのx座標が渡ってくる
// 仮に横幅が 800px なら 0 ~ 800 の値が gl_FragCoord.x の範囲になる
// fract(gl_FragCoord.x / 100.0) は 0 以上 1 未満
float red = fract(gl_FragCoord.x / 100.0);
では上記では何をしているのだろうか。
gl_FragCoord.x
にはピクセルごとのx座標が渡ってくる。本記事ではCanvas
の横幅がそのまま横幅の値になるのだが、デバイスやブラウザの画面幅によってCanvas
のサイズは異なる。一応本記事ではCanvas
が最大でも800pxになるようにコーディングしているので、gl_FragCoord.x
の値は0 ~ 800の間である(デバイスや画面幅によっては最大値が小さくなる)。
したがってgl_FragCoord.x / 100.0
は0 ~ 8であり、fract(gl_FragCoord.x / 100.0)
はx軸に基づいて、0以上1未満の値が100pxごとに、最大8回繰り返されることとなる。この値をred
としてgl_FragColor
に格納しているので、グラデーションが繰り返される模様となった。
次にred = gl_FragCoord.x / Canvasの幅
としてグラデーションを実装したい。これができればred
はCanvas
の幅に関係なく、左端が0、右端が1となる。つまり左から右にかけて、黒からだんだん赤になるグラデーションを実装できる。
これを実現するためには、uniform
を使ってJavaScriptからGLSLにCanvas
の大きさを渡せば良いのだが、ここで「WebGLProgram – three.js docs」を読んでみる。
頂点シェイダーにはattribute vec2 uv;
が定義されているのがわかる。これはThree.jsがビルドインで用意しているUV座標であり、まさに先ほど欲しかったgl_FragCoord.x / Canvasの幅
の値に合致する。厳密言うとThree.jsにおけるUV座標は(x, y)の2次元座標であり、左下を原点(0, 0)として、右上が(1, 1)となる座標である。
UV座標は頂点シェイダーにしか用意されていないのでvarying
を使ってフラグメントシェイダーに渡す必要がある。
黒から赤に変化するグラデーションの実装ができた。簡単にコードをみてみる。
varying vec2 vUv;
void main () {
vUv = uv;
//...
}
UV座標をフラグメントシェーダーに渡すために、頂点シェーダーでvarying vec2 vUv;
を宣言している。main
の中でuv
をvUv
に格納した。
varying vec2 vUv;
void main () {
float red = vUv.x;
//...
}
フラグメントシェーダーでもvarying vec2 vUv;
を宣言し、UV座標を受け取る。そのうちのx座標をred
に代入しているので、x軸に基づいて黒から赤になるグラデーションとなった。