3D一般 ノーマルマップの互換性問題(2) UVに潜む罠

前回の続きです。
3D一般 ノーマルマップの互換性問題(1) ノーマルマップとは

●ノーマルマップに格納されるベクトルの座標系

ハイトマップからノーマルを生成する場合を考えてみます。
nVIDIA の Photoshop dds Plug-in にこの機能がついており
簡単に生成することができます。同様のツールは他にもあるし
いまどきのシェーダーならリアルタイムに生成するかもしれません。

まず基準となる座標系を決めておきます。例えば U-V を X-Y
平面とし、uv が左上原点の場合軸の向きを次の方向に想定します。

 U = X軸 + 方向
 V = Y軸 – 方向

右手座標系なら Z軸は手前(カメラ方向)が + です。
法線は -1.0~+1.0 の範囲の値を取るので、色情報として格納する
ために 0~1.0 に変換します。
8bit の場合はこれが 0~255 に相当します。

 R= X * 0.5 + 0.5
 G= Y * 0.5 + 0.5
 B= Z * 0.5 + 0.5

平面は常に手前の Z+ 方向を向いているので、法線の Z軸が負に
なることはなく常に 0.5~1.0 の範囲となります。

そのため画素値には必ず「青色」が一定量含まれており、作られた
ノーマルマップは一般的に青色に見えます。

完全に滑らかで凹凸の無い平面の場合は、どの法線も手前を
向いているので (0,0,1) になります。カラーにすると

 R= 0.5 = 128
 G= 0.5 = 128
 B= 1.0 = 255

の真っ青です。この X-Y 平面に配置した右手系ノーマルマップの
ことを別名「青マップ」と呼ぶことがあります。

一般的なタンジェントスペース用のノーマルマップのデータは
その多くが「青マップ」で、上記に近い座標系が使われる
ことが多いようです。

余談ですが、GeForce3 で私がはじめて作ったノーマルマップ用の
描画シェーダーは、何故か X-Z 平面で作っていて「緑マップ」
になっていました。当時ほかのツールなどの知識が皆無で、
何が標準なのか全く知らなかったので・・。

nVIDIA の Photoshop dds plug-in 等では、これらの軸を
入れ替えたり反転させたりと簡単に設定できるようです。
便利になりました。

TS (TangentSpace) 専用で用いる場合 Zが常に正なので
0~1.0 をそのままテクスチャに格納することもあります。

符号が不要なので、ATI の 3Dc や NVIDIA の Hi/Low フォーマット
など、法線圧縮を行う場合 X Y のみ格納します。
(Z は Z= sqrt(1-X*X-Y*Y) )

このように 2D で作成したノーマルマップのことを以下の説明で
「プレーンノーマルマップ」と呼ぶことにします。

●固定の座標系を持ったノーマルマップを任意の面に貼る

ノーマルマップは基準となる座標系に作られるので、必要に
応じて別の空間に変換する必要が生じます。

例えば X-Y 平面に作られた青マップを何も考えずに水面に貼ると、
法線は真横を向いているため真上の光源で暗くなってしまいます。
貼られた面のジオメトリに合わせて座標系を変換しなければ
なりません。

オブジェクトのローカル座標とノーマルマップが貼られた
UV を基準にした接空間(TangentSpace) の変換マトリクスを、
法線と同じように頂点単位で格納しておきます。回転なので
3×3 でよく、これはオブジェクト空間における接空間の 3つの
ベクトル軸に相当します。

 X = U = Tangent
 Y = V = Binormal
 Z = W = Normal

光源はワールド座標系に配置するので、ノーマルマップを用いて
光源計算を行う場合は下記の変換をたどります。
もちろん光源を逆変換してもかまいません。

 ノーマルマップの基準座標系
  ↓
 オブジェクトの座標系
  ↓
 ワールド座標系

TangentSpace 用のノーマルマップ (以下 TS Normalmap) は
データ作成時に決められた基準平面を持っており、自由に変換
できるので任意のポリゴン面に貼り付けることができます。
マップなど広い面積に適用する場合には、データを使いまわす
ことができるため TS Normalmap が必要です。

●オブジェクトスペースのノーマルマップとは

オブジェクトの表面の法線を、そのままテクスチャに書き込んだ
のが ObjectSpace (OS) のノーマルマップです。
(以下 OS Normalmap )
基準の面を持たず、オブジェクトに貼り付けた状態で正しい
ジオメトリとなります。

オブジェクトの形状に強く依存しているため、貼られた
テクスチャを他の形状のモデルや他の面に動かすことができません。

オブジェクトそのものの法線をそのまま表しているため、
すべての方向を向く可能性があります。X Y Z はどれも
-1.0 ~ +1.0 の値を取り、カラーで見るとさまざまな色が
混じった虹色です。

描画時は接空間との座標変換が不要で、TS Normalmap の描画と
比較すると頂点データサイズ減り GPU の負荷も低くなります。

データの流用ができないため、マップのように広い面積には
使えず、オブジェクトのような固定物に向いています。

キャラクタのような骨変形する物体に OS Normalmap を用いる
場合は、不可能ではないですがかなり注意が必要です。
基準となる形状を保存しなければならないので、ターゲットの
実機では使えても 3Dツール等では使えません。
以前のプロジェクトでは GPU が非力なので、少しでも処理を
稼ぐために OS も併用しました。
(この苦労話も機会があればいつか・・)

● OS Normalmap と TS Normalmap の相互変換

OS Normalmap と TS Normalmap は座標系が違うだけです。
それぞれ基準となる座標系さえわかれば相互変換は簡単にできます。
ハードウエアでもできます。

使い勝手や利便性を考えると、今の GPU なら TS Normalmap を
選ぶことになるでしょう。

● TS Normalmap は TangentSpace に依存する

TS Normalmap をどのようにポリゴン面に貼り付けるのか、
それを決定するのは頂点に埋め込まれた接空間マトリクス
(TangentSpace) です。

また TS Normalmap を作るツールも、UV と頂点座標から求めた
TangentSpace を元にデータを作成します。

TS Normalmap はオブジェクト空間に依存しない代わりに、
TangentSpace に強く依存してしまいます。

ノーマルマップを生成するツールの TangentSpace の算出と、
実際に描画するデータ内の TangentSpace が一致して
いなければ正しい結果になりません。

水面や垂直の壁など、わかりやすい平面の場合はあまり問題が
起きませんが、球状の物体など共有頂点でソフトエッジの
場合(または同じスムージンググループの場合)
TangentSpace はどうなるのでしょうか。

●本来の法線

ポリゴンで表現されたオブジェクトは、本来の形状を複数の
平面で近似したものです。平面故に曲面の表現は完全には
一致しません。見た目で誤差を埋めるために

・頂点の色を補間する
・光源計算の結果を補間する
・補間した結果を描き込んだ滑らかに見えるテクスチャを貼る

等の処理(ごまかし)が必要だったわけです。

頂点は離散的に本来の形状をサンプリングした座標値で、
同じように本来の形状の面の向きを、頂点位置でサンプリング
したのが頂点法線です。
共有される面法線の単なる平均ではなく、その点にあるべき
実際の面の傾きを表現しているといえます。

●共有頂点の TangentSpace

接空間も実形状の表面をなぞる向きに配置されていると考えられ
ます。法線の補間と同じように接空間も頂点間で補間します。

例えば球のノーマルマップを作成して 12分割程度のローポリゴン
モデルに適用することを考えます。

共有頂点のソフトエッジなら、接空間も補完によって法線同様
球状の表面に配置されます。補間された接空間と実モデルの法線
に差が無ければ、得られたノーマルマップはすべて (0,0,1) の
ベクトルを持つ真っ青でプレーンなマップになります。

●スムース補間問題

頂点法線が期待している形状と、接空間が作る表面形状にずれが
生じるとどうなるでしょうか。この状態でノーマルマップを作ると、
空間のずれをノーマルマップ側で吸収しようとします。
つまり

 ローポリ側の形状がノーマルマップに漏れ出している

わけです。
頂点法線と接空間を別計算で求め、お互いに整合性をとらない
場合に発生します。

接空間側で丸めるか、ノーマルマップ側で丸めるかの違いです。
ノーマルマップを作る上での考え方の違いといえるかもしれません。

ただし、このように漏れのある TangentSpace にはプレーン
ノーマルマップを貼ることができません。プレーンノーマルマップ
には、期待する形状の誤差を埋めてくれる情報が含まれていない
からです。

●UV ハードエッジ問題

UV は矩形のテクスチャをサンプリングする座標で 0~1 の範囲を
持った有限の値です。法線とは違い、シリンダなどに貼ると
必ず一周するつなぎ目ができます。

法線は共有し、かつソフトエッジだとしても (または同一の
スムージンググループに属しているとしても) 数値上 UV の値は
共有されていない非連続の状態ができます。

この条件において、法線ではつながっているにもかかわらず
接空間を UV と同じように非連続とみなしてしまうソフトが
あります。

つまり UV 値が非連続の場合、強制的に接空間だけ
ハードエッジになってしまうわけです。

例えば 3ds max 7 が生成するノーマルマップ用の接空間は、
UV の切れ目を常にハードエッジとみなしていました。

そのためシリンダや球面などの UV の接合面は、ノーマルマップ
側に曲面が描きこまれており、真っ青でプレーンな青マップに
なりません。部分的に UV の切れ目でグラデーションが発生して
しまいます。

3ds max 7 の中で閉じている分には同じ TangentSpace を使う
ため問題はありませんが、他のレンダラに持っていくと意図
した描画にならず切れ目が発生してしまうことがあります。

またこの場合もプレーンノーマルマップを貼る場合、切れ目の
部分だけおかしなことになります。
これも形状漏れの一種といえるでしょう。

●くるくるUV問題

単なるただの壁面に見えても、そこには作りこまれた職人の業が
あります。わずかなテクスチャを使い、リピートさせたり
回転させたり反転させたり、さまざまな貼り方を用いて
単調な繰り返しにならないように工夫されているのです。

技巧的なデザイナーは緻密に作りこまれた UV 値を持った
データを仕上げます。
ドット職人のドット技に似ているかもしれません。

ところがこのようなデータに対する耐性は多くのツールが
持っていません。

日本のゲーム会社でデザイナーが作ったデータの UV 値は、
反転は当たり前、回転も当たり前、テクスチャに対する UV の
オーバーラップもリピートも当たり前に使われていることが
あります。

複雑な UV を持ったデータでも、TangentSpace を求めて
ノーマルマップを貼ることができるかどうかが問題です。

レンダラによっては、UV を反転しただけでノーマルマップの
凸凹が逆になってしまうものもあるようです。

flip された UV で接空間が逆向きになり、共有時に相殺
されてベクトルが 0 に近づいてしまうこともあります。

ツールによって対応度が異なっており、例えば下記のような
問題があるようです。

・対策無しに補間してベクトルが 0 に近づいてしまう
   さらにその状態を無理やり正規化するので
   ねじれが発生するしそもそもつながっていない。

・ベクトルが一致しないため異なる頂点とみなして補間しない
   ハードエッジ化

上記問題が発生するため、もしかしたら開発チームによっては
UV 反転や回転を使ったデータの製作を禁止しているかもしれません。
伝え聞いた話では、デザイナーが手で修正してノーマルマップの
色を力技で書き換えているところもあったそうです。

UE3 や CryEngine など、有名どころではしっかり対応
しているのではないでしょうか。

私が くるくるUV問題 に遭遇したのは 5年ほど前で、実際に
開発していたコンシューマのプロジェクトでした。
(2004年に実際に発売されました)
その時はデザイナーの要望に応えつつ次のように対処しました。

・TangentSpace の符号が反転しているケースでも、
 共有頂点なら符号を残したまま同一直線状と見なして
 補間する

2年前に作った DirectX9 用エンジン (HYPERでんち のトップ絵等)
では、データ exporter が法線とは別に TangentSpace 専用の
スムージンググループを求めています。

●問題を踏まえて

このように、結構ツールによって生成される TS Normalmap
には差が生じています。

原因は特殊な UV 条件を想定していないことと、 ツール内で
完結している分には問題が表面化しないためだと考えられます。

次回はさらに、各種ツールの現状をいくつか取り上げてみたいと
思ってます。