9 海をつくる

7 ノイズをつくろう8 フレネル反射をつくる を組み合わせる。

パーリンノイズをハイトマップ(高さを定義したマップ)としてつかう。ddxddy という、部分微分(隣のピクセルとの差分)を取得できる関数をつかう。この外積(ベクトルとベクトルをあわせてつくったひし形に垂直なベクトル)を法線としてつかうことで、波立たせることができる。

normal = cross (
    float3(0,ddy(height),1),
    float3(1,ddx(height),0)
)

4 スクロールする水面を作る のように uv スクロールさせて波を動かす。

i.uv.x += 0.1 * _Time;
i.uv.y += 0.2 * _Time;

これで波そのものはできたが、エイリアシング(遠景の波が細かく表示され見た目が悪くなる現象)が発生する。

エイリアシングによって遠景の波が細かくなりすぎている

そこで、カメラと法線のベクトルの内積が 0 に近いほど急速に出力も 0 に近づいていく関数を導入する。今回はシグモイド関数をつかう。

σ(x)=11+ex\sigma(x) = \frac{1}{1+e^{-x}}

好きな形に近づけていく。

σ(x)=11+e60x+5\sigma(x) = \frac{1}{1+e^{-60 \cdot x + 5}}

xが0に近づくにつれて急速にyが0に近づくグラフ

フレネル反射で既に内積を求めているため、変数を拝借する。

//フレネル反射
half vdotn = saturate(dot(viewDir, i.normal));
...

//エイリアシングを防ぐための重みづけ
float weight = 1.0 / (1.0 + exp(-60.0 * vdotn + 5.0));

//パーリンノイズを生成
float height = noise(i.uv * 64) * weight;

これで、見た目の良い波ができた。

エイリアシングが消え、なだらかな波がたつ夜の海

Back Link