Skip to content

第08回 水面表現1:波と法線

前回: 第07回 ボリュームレイマーチ

今回の授業内容

  • 複数の方向を持つ波を合成する
  • 高さ関数を頂点変形と法線計算で共有する
  • 有限差分で水面の接線と法線を求める
  • フレネルで視線角度による反射を調整する
  • 正弦波とノイズを組み合わせて規則性を崩す

水面を構成する要素

水面は青い半透明マテリアルだけではありません。後期で学んだ技術を、次の役割に分けて統合します。

要素表現するもの
頂点変形大きな波とシルエット
法線細かな波と光の反射
フレネル斜めから見たときの反射
ノイズ周期的すぎる波の崩し
深度水深による色と透過の変化

今回は波、法線、フレネルを作り、深度と屈折は次回追加します。

方向のある波を作る

hlsl
float DirectionalWave(
    float2 positionXZ,
    float2 direction,
    float frequency,
    float speed,
    float amplitude,
    float time)
{
    float phase = dot(positionXZ, normalize(direction)) * frequency;
    phase += time * speed;
    return sin(phase) * amplitude;
}

波の方向と位置の内積を使うと、その方向に進む位相を作れます。複数の波を異なる方向、周波数、振幅で加算します。

hlsl
float WaterHeight(float2 p, float time)
{
    float height = 0.0;
    height += DirectionalWave(p, float2(1.0, 0.2), 1.2, 1.0, 0.18, time);
    height += DirectionalWave(p, float2(-0.3, 1.0), 2.1, 1.5, 0.08, time);
    height += DirectionalWave(p, float2(0.7, 0.7), 4.0, 2.2, 0.03, time);
    return height;
}

低周波の波は大きな起伏、高周波の波は細かな表面変化を担当します。

高さ関数から法線を求める

現在位置と X、Z 方向に少しずらした位置で高さを求め、2つの接線から法線を作ります。

hlsl
float3 WaterNormal(float2 p, float time)
{
    const float e = 0.02;
    float h = WaterHeight(p, time);
    float hx = WaterHeight(p + float2(e, 0.0), time);
    float hz = WaterHeight(p + float2(0.0, e), time);

    float3 tangentX = normalize(float3(e, hx - h, 0.0));
    float3 tangentZ = normalize(float3(0.0, hz - h, e));
    return normalize(cross(tangentZ, tangentX));
}

頂点変形と法線が異なる高さ関数を使うと、見た目と光の反射がずれます。共通の WaterHeight を使います。

フレネルで反射量を決める

hlsl
float ndv = saturate(dot(normalWS, viewDirectionWS));
float fresnel = pow(1.0 - ndv, fresnelPower);
float3 waterColor = lerp(baseWaterColor, reflectionColor, fresnel);

水面を真上から見ると水中が見え、水平に近い角度から見ると周囲の反射が強く見えます。今回は反射色で代用し、後で Reflection Probe や画面反射へ発展できる構成にします。

実習:波の層を組み立てる

  1. 細分化された Plane と URP Lit Shader Graph を用意する
  2. HLSL の WaterHeight を Vertex Position に加える
  3. 1方向の波で動作を確認する
  4. 方向と周波数の異なる3つの波を合成する
  5. 有限差分で法線を再構築する
  6. フレネルで基本色と反射色を混ぜる
  7. FBM を弱く加え、正弦波の規則性を崩す

授業内課題:3つの水面を作り分ける

  • 静かな池:低い振幅、細かい法線、強めの反射
  • 海:大きな波、複数方向、遠くで見える輪郭
  • 幻想的な水:色、波、ノイズを意図的に強調

各設定で、波の方向、周波数、振幅、フレネルの値を記録します。

完成条件

  • 複数方向の波で頂点を変形できる
  • 高さ関数から法線を再構築できる
  • フレネルで視線角度による色を変化させられる
  • 用途の異なる3種類の水面を比較できる

今回のまとめ

水面の大きな形は頂点、細かな光の変化は法線で表します。同じ高さ関数を共有し、フレネルで視線角度を加えることで、前期の頂点アニメーションを有機的な材質表現へ発展させます。

次回: 第09回 水面表現2:深度、屈折、泡