Shader Cubicle

TwitterGitHub
Tweet

グラデーションの実装

  • 頂点シェーダー
  • フラグメントシェーダー

今回はグラデーションの実装について考えてみる。

前回まではgl_FragColorに固定の値を格納していた。つまりすべてのフラグメントシェーダーで同じ値を実行していたので、単色のオブジェクトがレンダリングされていた。

一方でグラデーションを表現するためには、ピクセルごとに色を変える必要があるので、gl_FragColorに格納する値を変える必要がある。その値をどのように用意するのか。まずはgl_FragCoordについて考えてみる。

gl_FragCoordはGLSLの組み込み変数であり、対象となるピクセルの座標が入っている。gl_FragCoordvec4型ではあるが、gl_FragCoord.xyとすれば、x座標とy座標が取得できる。たとえば一辺が500pxの正方形だとすると、左下が(0, 0)、右上が(500, 500)の値となる。この時左下が原点であることに注意する。

では早速gl_FragCoordを使って、以下のようにfragmentShaderを実装してみる。黒から赤へのグラデーションが何回か繰り返されているのが確認できる。

fragmentShader

先にfractについて簡単に説明しておくと、fractはGLSLの組み込み関数で、x - floor(x)の計算をしてくれる関数である。「The Book of Shaders: fract」を読むとイメージがつきやすい。ようは値を渡すと少数部分を返してくれる関数であり、たとえばfract(1.5)0.5fract(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の幅としてグラデーションを実装したい。これができればredCanvasの幅に関係なく、左端が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を使ってフラグメントシェイダーに渡す必要がある。

vertexShader
fragmentShader

黒から赤に変化するグラデーションの実装ができた。簡単にコードをみてみる。

varying vec2 vUv;

void main () {
  vUv = uv;
  //...
}

UV座標をフラグメントシェーダーに渡すために、頂点シェーダーでvarying vec2 vUv;を宣言している。mainの中でuvvUvに格納した。

varying vec2 vUv;

void main () {
  float red =  vUv.x;
  //...
}

フラグメントシェーダーでもvarying vec2 vUv;を宣言し、UV座標を受け取る。そのうちのx座標をredに代入しているので、x軸に基づいて黒から赤になるグラデーションとなった。

参考

前の記事へ次の記事へ