still water effectに関する追加説明
概要
自作のゲームエンジン開発を目指し、Mechtatelというプロジェクトを進めています。
詳細はGitHubのreadmeを確認してみてください。
最近Claude Codeに頼んでstill water effect (静水エフェクト)を実装してもらいました。
「静水」なのは、波の振幅や歪みを大きくしすぎると描画がバグるからです。
ここで問題になったのが、Claude Codeが出してきたシェーダーのコードを、管理人が一見しただけでは理解できなかったことです。
コードの内容を理解しないままお出しすることも可能ですが、それをやってしまうと今後の修正や機能追加が難しくなってしまうので、Claude Codeに確認しつつ自分なりに理解することにしました。
この記事で紹介するのは、管理人が一見しただけでは理解できなかった、水面の法線ベクトルを求める処理です。
この記事の執筆段階ではまだPRをマージしていませんが、近いうちにマージすると思います。
以下、PRと該当ファイルのリンクです。
水面の法線ベクトルを求める
曲面$F(x,y,z)=0$を考えます。
今回のシミュレーションでは、波による水面の高さの変化は$y’=h(x,z)$なので、
$$
F(x,y,z)=y-h(x,z)
$$
とします。
ちょうど水面上にある点の場合は$y=h$より$F=0$となります。
水面より上($y>h$)なら$F>0$、逆に水面より下($y<h$)なら$F<0$となります。
曲面$F$の法線ベクトル$\boldsymbol{n}$は勾配$\nabla F$を用いて以下のように表されます。
$$
\boldsymbol{n}=\frac{\nabla F}{\left| \nabla F \right|}
$$
今回の場合、勾配$\nabla F$は以下のようになります。
$$
\nabla F=\left( \frac{\partial F}{\partial x},\frac{\partial F}{\partial y},\frac{\partial F}{\partial z} \right)=\left( -\frac{\partial h}{\partial x},1,-\frac{\partial h}{\partial z} \right)
$$
次に、$\frac{\partial h}{\partial x}$と$\frac{\partial h}{\partial z}$を求めていきます。
今回のシミュレーションでは、$h(x,z)$は以下のように表されます。
$$
h(x,z)=Aw_A\sin\left[ r(x,z)fw_f+tv \right]
$$
| パラメータ | 説明 |
|---|---|
| $A$ | 振幅 |
| $w_A$ | 振幅のウェイト |
| $r$ | $xz$平面について、原点から波の進行方向への距離 |
| $f$ | 周波数 |
| $w_f$ | 周波数のウェイト |
| $t$ | 時間 |
| $v$ | 波の速さ |
ここで、$\boldsymbol{d}_{\text{wave}}$を波の進行方向とすると、$r(x,z)$は、
$$
r(x,z)=\boldsymbol{d}_{\text{wave}}\cdot\begin{pmatrix} x\\z \end{pmatrix} =d_{\text{wave}\_x}\cdot x+d_{\text{wave}\_z}\cdot z
$$
となります。
したがって、
$$
\begin{align}
\frac{\partial h}{\partial x}&=\frac{\partial}{\partial x}Aw_A\sin\left[ r(x,z)fw_f+tv \right] \\
&=Aw_A\cos\left[ r(x,z)fw_f+tv \right]\cdot\frac{\partial}{\partial x}\left[ r(x,z)fw_f+tv \right] \\
&=Aw_A\cos\left[ r(x,z)fw_f+tv \right]\cdot fw_f\frac{\partial}{\partial x}r(x,z) \\
&=Aw_A\cos\left[ r(x,z)fw_f+tv \right]\cdot fw_fd_{\text{wave}\_x}
\end{align}
$$
同様の計算で、
$$
\frac{\partial h}{\partial z}=Aw_A\cos\left[ r(x,z)fw_f+tv \right]\cdot fw_fd_{\text{wave}\_z}
$$
以上より、水面の法線ベクトルを求めることができます。
勾配ベクトルが法線になること(具体例)
管理人は勾配ベクトルが法線になることを初見では理解できなかったので、ここに具体例を挙げておきます。
一般的な場合の証明が気になる方は各自で調べてもらうとして、ここでは2次元平面での具体例を紹介します。
例として、$f(x,y)=x^2+3y^2$の勾配を求めてみます。
勾配$\nabla f$は、
$$
\nabla f=\left( \frac{\partial f}{\partial x},\frac{\partial f}{\partial y} \right)=(2x,6y)
$$
となります。
試しに$(x,y)=\left( \frac{1}{2},\frac{1}{2} \right)$での勾配を求めて、それをグラフにしてみます。
$\nabla f\left( \frac{1}{2},\frac{1}{2} \right)=(1,3)$なので、この点を通る勾配の直線は$y=3x-1$です。

確かに法線ベクトルになっていそうです。