さやちゃんぐbotブログ

シェーダーで遊んだりするブログです。

stepであそぼう!~stepはシェーダーで一番楽しい関数。たぶん。~

こん●●は。さやちゃんぐbotです。

 

この記事はシェーダーアドベントカレンダー6日目の記事として書かれています。

qiita.com

5日目は「KodeLifeでレイマーチングを動かす。」でした。

sayachang-bot.hateblo.jp

 

今日の記事はシェーダー関数のstepについて扱っていきます。

stepというのは、シェーダー界隈では手垢のついた枯れた話題ではあります。ただ、シェーダーというものは人によって知識、工夫といったものに違いがあるジャンルでもあります。

私なりのやり方ではありますが、この機会に「stepってどう使うことができるのか」というのをまとめてみるのも面白いかも?ということで書き残してみます。


それでは始めていきましょう!

コードスニペットScrapBoxにまとめてありますので、一覧を眺めたい場合にはこちらをご覧ください。

scrapbox.io

さて、順に進めて行きましょう。 

 

分岐

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)を図示すると以下のような具合です。

f:id:sayachang_bot:20191205193319p:plain

値aからbの範囲で、与えられた0.0から1.0の割合に応じて中間の値を返します。この補間の方法が線形なのです。

smoothstep(a, b, x)では、補間のやり方がエルミート補間と呼ばれるものです。エルミート補間って何?というのは、イメググってグラフを眺めてみて、まずは感覚をざっくり理解してみるのが良いでしょう。

f:id:sayachang_bot:20191205194047p:plain

エルミート補間は3次関数で表現します。計算式にするとこうです。

f:id:sayachang_bot:20191205194741p:plain

「ゆるやかな変化」が欲しくなる場面はたくさんあります。線形補間だといまいちと感じた時は、以下のシェーダーコードを試してみると幸せになれるかもしれません。このロジックはシェーダーに限らず、ゲームエンジンモーショングラフィックスなど様々な局面で役立つことでしょう。
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)

f:id:sayachang_bot:20191205200203p:plain

 

四角形は以下のようになります。
step(max(abs(p.x), abs(p.y)), size)

f:id:sayachang_bot:20191205200359p:plain

 

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種類の絵に表示が変わります。

f:id:sayachang_bot:20191205201759p:plain

f:id:sayachang_bot:20191205201822p:plain

 

glitch

画面がバグったような見た目になるグリッチにstepを使ってみましょう。

時間を掛け算してstepを使えば、チラチラとした見た目を作り出すことができます。

step(.88,fract(time*33.))

 

これにランダムな値を組み合わせ、座標を歪ませ、色を変えてみましょう。

https://scrapbox.io/api/code/sayachang/step/stepGlitch.GLSL

f:id:sayachang_bot:20191205210716p:plain

 

いかがでしたでしょうか。

シェーダーにおいてstepは数値を操作するのにたいへんいろいろな局面で使われます。時間、座標、色のあらゆる加工でstepは重宝します。

興味を持った方は、ぜひstepで何かを作ってみてください。

 

アドベントカレンダー明日7日目の記事は、土屋つかささんによる「unityシェーダー周りでなんか書きます」です。

 (ここに後でリンクを貼ります)

 

引き続き、シェーダーアドベントカレンダー2019をお楽しみください!

ではでは。