stepであそぼう!~stepはシェーダーで一番楽しい関数。たぶん。~
こん●●は。さやちゃんぐbotです。
この記事はシェーダーアドベントカレンダー6日目の記事として書かれています。
5日目は「KodeLifeでレイマーチングを動かす。」でした。
今日の記事はシェーダー関数のstepについて扱っていきます。
stepというのは、シェーダー界隈では手垢のついた枯れた話題ではあります。ただ、シェーダーというものは人によって知識、工夫といったものに違いがあるジャンルでもあります。
私なりのやり方ではありますが、この機会に「stepってどう使うことができるのか」というのをまとめてみるのも面白いかも?ということで書き残してみます。
それでは始めていきましょう!
コードスニペットはScrapBoxにまとめてありますので、一覧を眺めたい場合にはこちらをご覧ください。
さて、順に進めて行きましょう。
分岐
step関数は、第一引数より第二引数が大きい場合に1を、それ以外は0を返す関数です。
if文で見てみましょう。
if(x <= 0.1) {
return 1.0;
} else {
return 0.0;
}
これと同じことをstepで書くと、以下のようなシンプルな実装になります。
return step(x, 0.1);
これを元に、いろいろな論理をstepで実装してみましょう。
・x >= c
step(c, x);
上でifを書き換えたものと同じです。
・x < c
1.0-step(c, x)
符号を逆にするには、1.0から引けばいいだけです。
・x != c
abs(sign(x - c));
not equalの実装は、sign関数と組み合わせるとシンプルに実装できます。
sign関数は引数の正負に応じて+1.0か-1.0を返しますが、ゼロの場合は0.0になります。absすればstepの挙動に乗っかった上で、最終的に0.0か1.0を返すことになります。
・x == c
1.-abs(sign(x - c));
上に挙げたものを組み合わせて、stepの挙動にのっけたものです。
・X && Y
step(X)*step(Y);
ANDの実装は掛け算をするだけです。XやYの部分には任意の計算式や、関数の戻り値を与えることができます。
・X || Y
step(0.5, step(X)+step(Y));
ORは評価したいstepの結果をすべて足してしまえば、どれかひとつが真の場合に1.0以上になることを利用します。
最後に全体をもうひとつのstepで囲い、「0.5より大きい」というような判断を入れてしまえば0.0か1.0を返すような構成にできます。
「if文をstepに書き換える」というのはやってみるといい頭の体操になりますので、パズルを解いて遊ぶつもりで取り掛かると楽しいです。
smoothstep
stepは0.0か1.0を返しますが、緩やかに補間された結果が良いルックになるケースがあります。これを実現してくれるのがsmoothstep関数です。
少しsmoothstepについて見てみましょう。
GLSLのmix関数かHLSLのlerp関数を知っているなら、smoothstepもすぐに使えるようになるでしょう。mix/lerpは線形補間を行う関数です。
mix(a, b, x)またはlerp(a, b, x)を図示すると以下のような具合です。
値aからbの範囲で、与えられた0.0から1.0の割合に応じて中間の値を返します。この補間の方法が線形なのです。
smoothstep(a, b, x)では、補間のやり方がエルミート補間と呼ばれるものです。エルミート補間って何?というのは、イメググってグラフを眺めてみて、まずは感覚をざっくり理解してみるのが良いでしょう。
エルミート補間は3次関数で表現します。計算式にするとこうです。
「ゆるやかな変化」が欲しくなる場面はたくさんあります。線形補間だといまいちと感じた時は、以下のシェーダーコードを試してみると幸せになれるかもしれません。このロジックはシェーダーに限らず、ゲームエンジンやモーショングラフィックスなど様々な局面で役立つことでしょう。
float t = clamp( (x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
図形を描く
シェーダーでお絵描きする場合、stepはとても役に立ちます。
例えば円は距離関数の値をstepで判断し、大きさを指定して着色すれば実現できます。
step(length(p), size)
四角形は以下のようになります。
step(max(abs(p.x), abs(p.y)), size)
2つの色から片方を選択する
時間に応じて色を切り替えてみましょう。
時間のmodを取り、それをstepで分割することができます。一定時間で交互に色がaとbとの切り替えを繰り返す実装は以下のようになります。
float t = mod(time, 4.0);
step(t, 2.0) * a + step(2.0, t) * b;
実際に動かしてみましょう。瑠璃色の青海波(るりいろのせいがいは)と紺の紗綾形(こんのさやがた)を切り替えてみます。
コードをここに置いてありますので、手元環境で実際に動かして確認してみてください。
https://scrapbox.io/api/code/sayachang/step/SeigaihaAndSayagata.GLSL
2秒ごとに2種類の絵に表示が変わります。
glitch
画面がバグったような見た目になるグリッチにstepを使ってみましょう。
時間を掛け算してstepを使えば、チラチラとした見た目を作り出すことができます。
step(.88,fract(time*33.))
これにランダムな値を組み合わせ、座標を歪ませ、色を変えてみましょう。
https://scrapbox.io/api/code/sayachang/step/stepGlitch.GLSL
いかがでしたでしょうか。
シェーダーにおいてstepは数値を操作するのにたいへんいろいろな局面で使われます。時間、座標、色のあらゆる加工でstepは重宝します。
興味を持った方は、ぜひstepで何かを作ってみてください。
アドベントカレンダー明日7日目の記事は、土屋つかささんによる「unityシェーダー周りでなんか書きます」です。
(ここに後でリンクを貼ります)
引き続き、シェーダーアドベントカレンダー2019をお楽しみください!
ではでは。