Saturday, October 7, 2017

HLSLでゲルストナー波を実装

NVIDIAのIsland11サンプルを改造してゲルストナー波を加えてみました。

Copyright (c) 2011 NVIDIA Corporation.


ゲルストナー波の実装例はいくつかのサイトで紹介されていますが、多くはGPU GemsのEffective Water Simulation from Physical ModelsのEquation 9の式が引用されています。

以下はこの式をHLSLで実装してみたものです。
struct Wave
{
float2 dir;
float amplitude;
float waveLength;
};
cbuffer cb2 : register(b2)
{
Wave waves[100];
};
static int numWaves = 20;
static float steepness = 0.8;
static float speed = 15;
float3 CalcGerstnerWaveOffset(float3 v)
{
float3 sum = float3(0, 0, 0);
[unroll]
for (int i = 0; i < numWaves; i++)
{
Wave wave = waves[i];
float wi = 2 / wave.waveLength;
float Qi = steepness / (wave.amplitude * wi * numWaves);
float phi = speed * wi;
float rad = dot(wave.dir, v.xz) * wi + g_time * phi;
sum.y += sin(rad) * wave.amplitude;
sum.xz += cos(rad) * wave.amplitude * Qi * wave.dir;
}
return sum;
}
引数のvで海面の頂点座標を受け取り、その位置においてどれだけ頂点を移動させればいいのかオフセットを返しています。C++側から乱数で生成した100個の波のパラメ ータを受け取っていますが、試行錯誤の結果20個だけ使っています。

C++でパラメータを生成するコードは以下です。

struct Wave
{
Vec2 dir;
float amplitude;
float waveLength;
};
struct ImmutableCB
{
Wave waves[100];
} cb;
for (int i = 0; i < dimof(cb.waves); i++)
{
Wave& w = cb.waves[i];
float randomRad = (float)(Random() * M_PI * 2 * 0.3f);
w.dir.x = sinf(randomRad);
w.dir.y = cosf(randomRad);
w.amplitude = 0.03f + powf(2.0f, (float)Random() * 2.0f) * 0.05f;
w.waveLength = 1.0f + powf(2.f, 1.f + (float)Random()) * 10.f;
}
view raw make_waves.cpp hosted with ❤ by GitHub
randomRadの計算で、最後の0.3fを無くすと全方向への波をランダムに生成することになります。0.3を掛けてある程度似た方向に向けて生成するようにしています。

amplitudeは波の高さで、waveLengthは波の幅です。特に決まった式があるわけではなく、見た目から数値や式を適当に選んだ結果です。ただし、GPU GemsによるとwaveLengthは中央値を基準に半分の長さから2倍の長さの波を作るといいと書いてあります。

下のgifはオリジナルのIsland11の波です。RGBをノーマル、Aをハイトマップとしたテクスチャを元に頂点を移動するだけというシンプルなアルゴリズムですが、このテクスチャの出来がいいのでとてもきれいです。

Copyright (c) 2011 NVIDIA Corporation.


波による頂点移動処理はドメインシェーダ内あり、そこをゲルストナー波に置き換えてみます。うねる波が表現できた半面、海らしい荒さが足りない気がします。

Copyright (c) 2011 NVIDIA Corporation.


そこで、Island11のオリジナルの波と合成してみると、それなりに見られるものになりました。結局ゲルストナー波単体できれいな絵はできず、さざ波や白波の表現も工夫していく必要がありそうです。

Copyright (c) 2011 NVIDIA Corporation.


次回は法線を求めてみます。

References:
Effective Water Simulation from Physical Models
Ocean Shader with Gerstner Waves
GERSTNER WAVE IMPLEMENTATION
DX11 Samples

No comments:

Post a Comment