3Dを扱う時の法線マップについての備忘として初心者向けにもわかるように基礎の基礎をまとめました。3D ゲーム開発などで役に立ちますように!
Unity 等のゲーム開発に限らず、Blender 等の3D モデリングソフト等、3D関連でなにかと登場してくる用語かと思います。
あるポリゴンの面が向いている方向(ベクトル)を面の法線ベクトルと呼びます。
ポリゴンの頂点に設定してある頂点の法線ベクトルです。
法線(英語ではnormal)が具体的に何に使われるかですが、3D 上で一枚のテクスチャ画像上に対しての光の差し込む方向と、法線マップ上の法線の方向(法線ベクトル)を元に計算することで、ポリゴン上の凹凸の陰影を表現することができます。
法線マップ(Normal Maps)はポリゴンに対して貼り付けできるテクスチャの画像データとは別に、専用のグレースケール画像データ等で割り当てできる法線ベクトルの集まりのデータのことです。
法線マップのプログラム上での取扱い 具体的に、法線マップは「ポリゴンの面が向いている方向に対してのベクトル」が「画像カラーのRGB成分に対してXYZ座標のベクトル成分が0~1.0の範囲で正規化されて2次元配列化された情報」であり、グレースケース画像などから変換して作ることができます。 Unity や UnrealEngine、Blender などの 3D エンジン側で処理してくれる機能が標準で搭載されてます。
下のグレースケース画像を作ってみました。黒が低く、白が高い、高低を表す画像です。このような画像を一般的にHeightMapと呼びます。
グレースケール画像(BMP画像)
これを法線マップの画像データに変換してみます。
※変換にはこちら(Christian Petry – Normal Map Online)のWebアプリを利用させていただきました。
サンプルの法線マップ(困り顔)画像
この法線マップを Unity などの中で割り当てることで、ポリゴン情報としてはまっ平な3Dオブジェクトだったとしても、割り当てた法線マップ(法線ベクトル)の向きに応じて、光の反射を計算して濃淡をつけることで、凸凹を表現することができます。
法線マップ画像が全体的に青紫色となっているのは、RGB色がそれぞれXYZ方向に対応しているためです。
赤色成分R→0~255(8Bit)の値を 法線ベクトルX成分の-1.0~1.0 (中心0.0)に標準化しています。
緑色成分G→0~255(8Bit)の値を 法線ベクトルY成分の-1.0~1.0 (中心0.0)に標準化しています。
青色成分B→0~255(8Bit)の値を 法線ベクトルZ成分の0.5~1.0 (中心0.5)に標準化しています。(平面は常にZ+方向を向いている)
先ほどのグレースケールの真っ黒部分(周囲もピクセルも黒で平面)の法線マップ変換は、
RGB(0,0,0) → 法線マップでのXYZ(0,0,1) → 法線マップRGB(128, 128, 255)
となり、だいたい青紫っぽい色となります。 このため通称「青マップ」と呼ばれることもあります。
上の画像をUnity上でただの四角形のポリゴンである平面オブジェクトの法線マップに設定するとこんな感じになります。
Unity 上で法線マップを適用した様子
※平面オブジェクトのテクスチャには茶色を設定し、スポットライトで照らしています。
ちゃんと、法線マップで白い部分(高い部分)がでっぱってるように見えますね! 法線マップをうまく活用すれば、ポリゴン数が少なくても、でっぱりがあるように見せることができます。
法線マップを使いたくないとき 法線マップが重たいなどの理由で使いたくない場合、あらかじめポリゴンに設定するテクスチャ画像に凹凸を描きこんで表現することで代用できます。ですが、その場合は別の角度からみても、光の差し込む方向で陰影が変化することがなくなります。遠くの方にある背景のようなオブジェクトなら、この手法でも問題ない場合が多いです。
法線マップ(Normal Maps)と似たもので、Height Maps があります。 Height Maps はグレーススケール画像で表現されることが多く、各ピクセルの単位で高さを表現することになります。
グレースケース画像上にClipStudioやPhotoshopの ブラシツールで高低を書いてあげれば、グレースケール画像を地形の高低に見立てて、ゲームなどに使える地形のメッシュデータに変換することもできますし、あるポリゴンに対して変化を加える、Bump(隆起)を表すデータとして使うこともできます。これをBump Mappingといいます。 Bump Mapping として使う場合は Normal Maps に変換されてポリゴン表面の濃淡として表現されます。
グレースケール画像の高低差は256段階 余談なのでですが、グレースケール画像を HeightMap として取り込もうとすると、地形のメッシュデータに変換したとき、画像の1ピクセルあたり、利用可能なデータが 256 段階しかないので、現実の国土地理院のデータとかを変換して表示しようとするとガタガタになったことがあります。 3Dレンダリングソフトが対応していればですが、まずは24bitカラーに変換してHeightMapとして取り込めばキレイになります。その際には元のグレースケール画像をぼかしフィルタなど画像処理してから利用するとよいです。(他にも乱数で補完するなどの方法があります)
Christian Petry – Normal Map Online
法線マップをグレースケール画像から生成できるWebアプリ。上の困り顔画像はこちらのサイトから生成しました。ドラッグ&ドロップするだけの簡単操作です。
更新日 | 更新内容 |
---|---|
2015.11.3 | ページ公開 |
2018.3.24 | スマホからレイアウトが崩れて読みにくいので修正。ついでに文章構造と内容も訂正。文章も自分で読んでイミフと思う箇所を若干修正。 |
Deepimpact 2018年4月19日 12:09 PM
青色成分B→0~255(8Bit)の値を 法線ベクトルZ成分の0.5~1.0 (中心0.5)に標準化
この部分で「0.5~1.0 (中心0.5)」とありますが、
中心が0.75の間違でしょうか?
0.5~1.0が0~1.0の間違いでしょうか?
あと、Strength、Levelあたりのアルゴリズムがわかるサイトがあれば
教えて頂けると助かります。
あまじ 2016年10月31日 10:00 PM
Deepimpactさま、こんにちは。コメントありがとうございます!ご指摘いただいた「0.5~1.0 (中心0.5)」については「0.0~1.0」の間違えです。さらにコメントで追記修正いたしました。
Strength、Levelについては、「非常にお役立ちとなるサイト」にリンクしたChristian PetryさんがMITライセンスで以下にコードを書いていますね。(Javascriptです)
https://cpetry.github.io/NormalMap-Online/javascripts/normalMap.js
コードをざっと読んだだけなので、間違いがあるかもしれませんが、
Christian Petryさんが定義している「Strength」「Level」はシェーダの計算式を読むと、法線テクスチャ画像(R,G,B)のBについて、
B:=1.0 / “Strengthで設定した値” * (1.0 + Math.pow(2.0, “Level”で設定した値)
で定義しているようですし、画面の2次元画像出力する際の画像フィルタ(Sobel Filterの3×3ピクセル、Scharr Filterの3×3ピクセル)でもStrengthとLevel値と同様の計算式用いて、隣接するピクセル同士を使ってのエッジ検出を行っているようです。
アルゴリズムは直接ここのコードを読むのが一番参考になるかと思われます。
https://cpetry.github.io/NormalMap-Online/javascripts/filters.js
お役に立てれば良いのですが。
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: