法線ベクトルの再計算

法線ベクトルの再計算

 シェーダで頂点を移動すると、元の法線ベクトルは使用できなくなります。そのため、法線ベクトル を再計算する必要があります。法線ベクトルは隣接する頂点もしくはハイトマップから計算することができます。

隣接する頂点を利用する方法

 法線ベクトルは、ある点とその近傍の二点の座標を用いることで求めることができます。 ある点 \(P\) における法線ベクトルを求めるには、点 \(P\) から接ベクトル方向へ少し移動した点\(P_{1}\)と従法線ベクトル方向へ少し移動した点\(P_{2}\)を求めます。これらの点から二つのベクトル\(P_{1}-P\)と \(P_{2}-P\) を求め、このベクトルの外積\((P_{2}-P)\times (P_{1}-P )\)を計算すると法線ベクトルが求まります。

座標系は左手系であるため、外積によって求められる法線ベクトル \(\vec{N}\) は上図の方向となっています(左手系において、外積によって得られるベクトルの方向は右手系の逆となる)。

shader

 パーリンノイズによって変形させたオブジェクトの法線ベクトルを再計算し、ランバート反射によるライティングを行っています。

tangent.wにDirectX(左手系)の場合は1、OpenGL(右手系)の場合は-1が入っています。このtangent.wを従法線方向のベクトルに掛けることで、 座標系が異なった場合でも 外積によって得られる法線ベクトルが一致するように処理できます。

実行結果

 上記シェーダの実行結果は以下の通りです。ノイズによって変形した球体にその形状に伴った陰影が得られていることがわかります。

また、法線ベクトルを表示すると以下のようになります。

ハイトマップから法線を計算する方法

 Unityにおいては、ハイトマップはTextureTypeをNormal Mapへ変更し、Create from Grayscaleにチェックを入れることでノーマプマップへ変換することができます。しかしながら、波紋のようにリアルタイムで変化する場合はシェーダでハイトマップから法線ベクトルを計算する必要があります。この法線ベクトルは下図に示すように、あるテクセルに隣接するテクセルの情報を利用することで計算できます。

 \(V_{x1}\) 、\(V\)及び\(V_{x2}\)の三点を結ぶ二次曲線を求め、Vにおける二次曲線の傾き\(du\)を求めるます。 同様に、 \(V_{y1}\) 、\(V\)及び\(V_{y2}\)の三点を結ぶ二次曲線を求め、Vにおける二次曲線の傾き \(dv\)を求めます。

$$ du = \frac{v_{x2}-v_{x1}}{2},\ dv = \frac{v_{y2}-v_{y1}}{2} $$

これらの傾きから\(x\)方向及び \(y\)方向の接ベクトルがわかります。 Unityではy軸が上方向ですが、法線マップではz軸が上となります。そのため、それぞれのベクトルは以下のようになります。

$$ \vec{T_{x}}=(1,\ 0,\ du),\ \vec{T_{y}}=(0,\ 1,\ dv)\\ $$

これらの接ベクトルの外積を求めることで法線ベクトル\(N\)が求まります。

$$ \begin{align} \vec{N}&=\vec{T_{x}}\times \vec{T_{y}}\\ &=(-du,\ -dv,\ 1)\\ \end{align} $$

詳しい導出過程は○×(まるぺけ)つくろーどっとコム:その3 波:ハイトマップから法線マップを作る方法 に記載されています。

shader

 波動方程式を用いた波紋の作成によって得られるハイトマップ(_HightMap)から法線ベクトルを計算し、平面の鏡面反射(_ReflectionTex)によって得られるテクスチャを法線ベクトルによって歪めることで、鏡面反射となっている平面が波紋によって波打つ様子を描画しています。また、BumpScaleによって歪む量を調節することができます。

実行結果

 実行結果は以下の通りです。波紋によって求められた法線ベクトルによってReflectionTexが歪められていることがわかります。

参考ページ

Unity User Manual:法線マップ(Normal Map)(Bump mapping)

○×(まるぺけ)つくろーどっとコム:その3 波:ハイトマップから法線マップを作る方法

 このコンテンツはユニティちゃんライセンス条項の元に提供されています