はじめに
今回はディスタンスフィールド(距離関数)を学び,以下のようなお絵かきをすることを目的にやっていこうと思います。
ディスタンスフィールドとは?
ディスタンスフィールド(距離関数)とは2点間の距離を返す関数のことです。
distance(p0, p1) // 点p0と点p1との距離を返す
距離関数を使ってみる
まずはイメージを掴むためにも,距離関数で得た値をそのまま色として出力してみましょう。
void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // 左下(0,0) ~ 右上(1,1) になるように正規化 vec2 uv = fragCoord/iResolution.xy; // 画面中央からの距離を求める float d = distance(vec2(0.5, 0.5), uv); // 距離を色として出力 fragColor = vec4(d, d, d, 1); }
出力画像をみてみると,外側にいくにつれて白色になっています。
これは距離が遠いほどd
の値が大きくなる(中心がで,四角が)ので,(R, G, B)
のそれぞれの値が大きくなり白色になったというわけです。
楕円を描画してみる
次にstep
関数というものを使う事で,簡単に円を描画することができます。
step(edge, x) // xの要素のうち、edgeよりも小さいものは0.0,それ以外を1.0を返す
先ほどの距離関数と組み合わせてみます。
void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // 左下(0,0) ~ 右上(1,1) になるように正規化 vec2 uv = fragCoord/iResolution.xy; // 画面中央からの距離を求める float d = distance(vec2(0.5, 0.5), uv); // 閾値を0.3としてstep関数を適応 float edge = 0.3; float color = step(edge, d); // 距離を色として出力 fragColor = vec4(color, color, color, 1); }
縦横のピクセル数が異なるので,私の場合は横長の楕円が出力されました。
また色を反転させたいときは、step(edge, 1.0 - d)
とすることでできます。
float edge = 0.8; float color = step(edge, 1.0 - d);
動きを付けてみる
uniform変数の一つであるiTime
(シェーダーの再生時間)を使う事で動きをつけることができます。
// 閾値を0 ~ 0.5に時間変化させる float edge = abs(sin(iTime)) * 0.5; float col = step(edge, d);
またcol
を1- col
に変更することで色を反転させられます。
線を描画してみる
以下の二つの円を掛け合わせることで,線を描画することができます。
float circleA = step(edge, d); float circleB = 1.0 - step(edge + 0.01, d);
この2つの楕円を組み合わせるとこんな感じになります。
float edge = abs(sin(iTime)) * 0.5; float col = step(edge, d) * (1.0 - step(edge + 0.01 , d));
仕組みとしてはstep(edge, d)
と1.0 - step(edge + 0.01, d)
の両方が1
の箇所が1
として出力されます。
しましま模様を書いてみる
次にsin
関数を上手に使う事で、しましま模様を書いてみようと思います。
float edge = 0.3; float color = step(edge, sin(d * 100.0 + iTime * 20.0));
仕組みはsin
波のedge
以上を白,小さいものを黒をして出力しています。
図形をいっぱい描画する
入力された座標を少し加工すると図形を複数描画することができます。
void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // 左下(0,0) ~ 右上(1,1) になるように正規化 vec2 uv = fragCoord /iResolution.xy; // 座標を加工する vec2 st = uv * 10.0; st = fract(st); // 画面中央からの距離を求める float d = distance(vec2(0.5, 0.5), st); float edge = 0.3; float col = step(edge, sin(d * 20.0 + iTime * 5.0)); // 距離を色として出力 fragColor = vec4(col, col, col, 1.0); }
入力されたuv
をfract
関数を用いて繰り返すことで実現をします
fract(x) // xの小数を返す
さいごに
今回はディスタンスフィールド(距離関数)を使って色々とお絵かきしてみました。
これからもちょくちょくシェーダー関連をあげるかもしれないので、よければお付き合いください。
ではまた。