Kilimanjaro Warehouse

WEBとかゲーム開発のことについて書きます。

勉強メモ: 市松模様を描くシェーダーについて

f:id:kilimanjaro-a2:20181017214042p:plain

市松模様(チェッカー柄)で画面を埋めるといったことがしたかったので、
こちらの記事のシェーダーを使わせていただいた。
qiita.com

このシェーダーが何をしているかについて勉強したときのメモ。

このシェーダーは基本Surfaceシェーダーに、パラメータとsurf関数内に下記の記述を加えたもの。

if (int(fmod(floor(IN.uv_MainTex.x * _Division) + floor(IN.uv_MainTex.y * _Division), 2.0)) == 0) {
    discard;
} 

上記の記述では、条件に一致した座標にあるピクセルの場合、処理を中断して、ピクセルを描画しないようにしている。
一行になっていてわかりづらいので、変数に分けるとこのようになる。

float x_value = floor(IN.uv_MainTex.x * _Division);
float y_value = floor(IN.uv_MainTex.y * _Division);
float surplus = fmod(x_value + y_value, 2.0);
if(int(surplus) == 0){
    discard;
}

簡単にいうと、UV座標のxとyに対して、分割数に応じた値を掛けたものを合計し、
その合計が2.0で割り切れた場合はそのピクセルを描画せず、割り切れなかった場合は描画するようにしている。

x_valueとy_valueの合計が2.0で割り切れるためには、
x_valueとy_valueが共に奇数、または共に偶数になる必要がある。
片方が奇数で片方が偶数の場合、これは2.0で割り切れない。

自分でも何を言っているかよくわからなくなってきたので、図を使って説明する。
とりあえず、分割数_Divisionが2の場合を考えてみる。
その場合、市松模様はこのような感じになる。
f:id:kilimanjaro-a2:20181017214039p:plain
UV座標は縦と横にそれぞれ0から1までの値を取る。
それを2つに分割するので、1÷2 = 0.5ごとに描画するかどうかを決めるxとyの閾値が決まる。
f:id:kilimanjaro-a2:20181017214038p:plain
ここでは、①と④の範囲は描画されず、②と③の範囲のみが描画される。


①の範囲ではx_valueが0、y_valueが0になる。
実際にそうなるかどうか、先ほどのコードに具体的な値をいれて確かめてみる。
適当にここでは(x, y) = (0.4, 0.2)という点で試してみる。

// 0.4 * 2は0.8なのでfloorで切り捨てられ、値は0となる
float x_value = floor(0.4 * 2);
// 0.2 * 2は0.4なのでfloorで切り捨てられ、値は0となる
float y_value = floor(0.2 * 2); 
//fmod(a, b)はaをbで割った余りを返す。0を2.0で割った余りは0
float surplus = fmod(x_value + y_value, 2.0)
if(int(surplus) == 0){//int(surplus)の値は0となるので条件と一致する。
    discard;
}

同じように計算すると、④の範囲ではどの点でもx_valueが1、y_valueが1になる。
合計が偶数となるので、2.0で割った余りは0となりif文の条件と一致してdiscardされる。

逆に②と③の合計は2.0で割り切れない値となるので、条件と一致せず普通に描画される。

分割数が増えていくと、閾値の位置が変わる。
分割数が3の場合、閾値は1/3 = 0.333...ごとに設定される。
分割数が4の場合、閾値は1/4 = 0.25ごとに設定される。
このように分割数が増えていけば、描画するしないの閾値が増えていく。