ゲルストナー波の法線を計算してみます。安直な方法としては、CalcGerstnerWaveOffset関数が波のあらゆる地点の頂点位置の移動量を求められるものであることを利用し、x方向とz方向に1センチずらしてそれぞれCalcGerstnerWaveOffsetを呼び、これを三角形に見立てて法線を求めます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
float3 ofsByGerstnerWave = CalcGerstnerWaveOffset(vertexPosition); | |
float3 dx = float3(0.01, 0, 0) + CalcGerstnerWaveOffset(vertexPosition + float3(0.01, 0, 0)); | |
float3 dz = float3(0, 0, 0.01) + CalcGerstnerWaveOffset(vertexPosition + float3(0, 0, 0.01)); | |
float3 N = normalize(cross(dz - ofsByGerstnerWave, dx - ofsByGerstnerWave)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
float3 CalcGerstnerWaveNormal(float3 P) | |
{ | |
float3 normal = float3(0, 1, 0); | |
[unroll] | |
for (int i = 0; i < numWaves; i++) | |
{ | |
Wave wave = waves[i]; | |
float wi = 2 / wave.waveLength; | |
float WA = wi * wave.amplitude; | |
float phi = speed * wi; | |
float rad = wi * dot(wave.dir, P.xz) + phi * g_time; | |
float Qi = steepness / (wave.amplitude * wi * numWaves); | |
normal.xz -= wave.dir * WA * cos(rad); | |
normal.y -= Qi * WA * sin(rad); | |
} | |
return normalize(normal); | |
} | |
float3 CalcGerstnerWaveBinormal(float3 P) | |
{ | |
float3 binormal = float3(1, 0, 0); | |
[unroll] | |
for (int i = 0; i < numWaves; i++) | |
{ | |
Wave wave = waves[i]; | |
float wi = 2 / wave.waveLength; | |
float WA = wi * wave.amplitude; | |
float phi = speed * wi; | |
float rad = wi * dot(wave.dir, P.xz) + phi * g_time; | |
float Qi = steepness / (wave.amplitude * wi * numWaves); | |
binormal.x -= Qi * wave.dir.x * wave.dir.x * WA * sin(rad); | |
binormal.z -= Qi * wave.dir.x * wave.dir.y * WA * sin(rad); | |
binormal.y += wave.dir.x * WA * cos(rad); | |
} | |
return normalize(binormal); | |
} | |
float3 CalcGerstnerWaveTangent(float3 P) | |
{ | |
float3 tangent = float3(0, 0, 1); | |
[unroll] | |
for (int i = 0; i < numWaves; i++) | |
{ | |
Wave wave = waves[i]; | |
float wi = 2 / wave.waveLength; | |
float WA = wi * wave.amplitude; | |
float phi = speed * wi; | |
float rad = wi * dot(wave.dir, P.xz) + phi * g_time; | |
float Qi = steepness / (wave.amplitude * wi * numWaves); | |
tangent.x -= Qi * wave.dir.x * wave.dir.y * WA * sin(rad); | |
tangent.z -= Qi * wave.dir.y * wave.dir.y * WA * sin(rad); | |
tangent.y += wave.dir.y * WA * cos(rad); | |
} | |
return normalize(tangent); | |
} |
あえてくどくどと関数を分けてありますが、論文で使われている式や変数名をなるべく残すためで、現場で使う場合は1ループで一気にfloat3x3の回転行列まで求めると良いと思います。
Island11のドメインシェーダの既存の法線はそのままワールド空間の法線として使われていましたが、それをゲルストナー波の接空間(tangent space)とみなしてワールド空間に配置します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
float3 gerstnerWaveN = CalcGerstnerWaveNormal(vertexPosition + ofsByGerstnerWave); | |
float3 gerstnerWaveB = CalcGerstnerWaveBinormal(vertexPosition + ofsByGerstnerWave); | |
float3 gerstnerWaveT = CalcGerstnerWaveTangent(vertexPosition + ofsByGerstnerWave); | |
float3x3 rotator; | |
rotator[0] = gerstnerWaveB; | |
rotator[1] = gerstnerWaveN; | |
rotator[2] = gerstnerWaveT; | |
output.normal = mul(water_normal.xyz, rotator); |
次回は水面に物を浮かべてみます。
No comments:
Post a Comment