はじめに
今回は、ノーマルマップの色の意味と、GLとDXの2つの形式の違い+変換方法について紹介します。

下記のように略して表示してます。
・GL → 「OpenGL」
・DX → 「DirectX」
また、今回の記事は私が出した本より図とテキストの一部を使います。

出版社さんからの許可をもらって画像を載せてます。
(私が書いた本です)
私の書いた本の中から、世の中的に困る人が多そうな事で…
ネットで見れる形で置いてた方がいいよねという情報をまとめる環境保全活動の一環。
ノーマル(法線)とは何か
ノーマル = 法線という意味です。
数学的な意味の法線は、線に垂直に交わる直線です。
ただ、3DCGのノーマル(法線)は別の意味になります。
具体的には下記の2つの意味になります。
【3DCGのノーマル(法線)の意味】
・面法線(面の向きを制御)
・頂点法線(陰影の見え方を制御)
↓の赤色が面法線、青色が頂点法線です。

まずは、この2つについて解説します。
面法線
3DCGで、面の向きを表す「面法線」です。
これは面の向きを決めるために使われます。
なので、面法線の向きは面に対して垂直にしかなりません。(裏か表か)

Blenderだと青色と赤色の面表示が一般的です。
(表面が青、裏面が赤)

設定を調整すれば、このように青色の線を表示することもできます。
(解説ブログを書かない限り、めったに使いません)

面の向きは、描画負荷を下げるための背面非表示の設定(バックカリング)などを使う際、
見え方に影響を与えます。

そして、次紹介する、頂点法線の自動設定を決める1つの要素に使われます。

面の向きを変えるとこのように、頂点法線の向きも変わります。

Blenderで面法線を見る方法、面の向きを変える方法などは詳細はこちらで解説してます。
頂点法線
3DCGで陰影の見え方を決定する「頂点法線」です。
これは影の見え方に影響を与えます。

頂点法線は面法線とは違い、向きを自由に設定できます。

そして、この頂点法線の向きを変えると陰影の見え方、影の表示が変わります。

大切なのは、面の形と陰影の形は「別のモノ」になることがあるという事です。
まずは、こちらを覚えてください。
ノーマルマップについて
色や陰影の向きに影響を与える頂点法線は…
特別な処理をしなければ、頂点にしかデータを入れれません。

この頂点以外、例えば面がある所に、色や頂点法線の情報を入れたら良いと思いませんか?

それを可能にする処理がノーマルマップです。

ノーマルマップを使うには…
3Dモデルの面の表面を2D形式に落とし込む必要があります。(UV展開)


動物がかわいそうですが…
例えるなら、アジの開きや動物毛皮の敷物のイメージです。
そして、この2D形式にしたデータは…
画像を使って色などの特定のデータを入力できます。
(テクスチャ)

これが俗にいう、カラーテクスチャです。
頂点が無い所(面)に画像で色情報を乗せてます。

そして、この画像による情報入力の仕組みを使って…
頂点法線を、頂点以外(面)で制御できるようにしたモノがノーマルマップです。

使うと、こんな感じで面の陰影の向きに変化が生まれます。
そして、疑似的に凹凸が入ってるような見え方になります。

以上が、ノーマルマップについての解説です。
なぜノーマルマップは青いのか
これを知るには、画像データの仕組みを知る必要があります。
画像はpx(ピクセル)という正方形の枠でできています。


1つの正方形のマスを1pxと表記します。
画像の大きさはこのpxの数で表します。
例:「2048px(縦)」×「1024px(横)」
そして、ディスプレイはドットという正方形の集まりで出来てます。
この1ドットには赤、緑、青の3色の長方形の発光体が入ってます。
この光り方の強度を調整することで、今あなたはディスプレイで色や文字を認識して見る事ができてます。

光の色は加法混色という混ざり方をします。
この3色の混ざり具合を調整することで、ほぼ全ての色を出せます。


黒色は… 純粋に色を入れない(光らさない)という形で表現します。
つまり画像データの正体は、この1ドットの光り方を調整するための情報です。
画像の1pxは、画面の1ドットに「赤、緑、青」の3つの発光体をどのぐらいの強度で光らすかを指定する情報が入ってます。

この発光の強度を指定する情報を複数並べる事で、画像データが完成します。

この発光体の制御情報が並んだ数、つまりpx数は画像の「大きさ」や「px」や「画素数」などと呼ばれます。
この色の強度は、PCの計算処理の関係で0~255段階で表されます。(8bitカラー)

このあたりの詳細は、こちらで解説してます。

そして、ノーマルマップはこの赤、緑、青のの情報を3つを “色” ではなく…
“頂点法線” の向きを表すために使ったモノになりますを

【ノーマルマップの赤、緑、青色の持つ意味】
赤(R)=X方向の傾き
緑(G)=Y方向の傾き
青(B)=Z方向、高さの変化の表示
基本的に赤と緑で傾きを表します。
128を基準に0~255の範囲で方向を指定します。


※8bit=256です。
そこから0をカウントするので255。
その255÷2を考えると、数値的には127が中間と思いますが…
こちらか調べ記事や行った実験ではノーマルマップにでは、
傾き無しの中間の赤色と緑色=128と表記されてました。
実際に凹凸無しのノーマルマップ画像から色をとっても128pxでした。
このあたりは、何かご都合主義の調整が入ったと考えください。
青の変化量はその傾き具合で自動的に数字が下がる形で決まるイメージです。
そして、ノーマルマップの青色はデータ処理の関係で127以下になりません。

つまり、ノーマルマップの青色は255~128の範囲しか使われておらず
青色の値が最初から大きいので、ノーマルマップは基本的に青くなります。

あと、ノーマルマップの基本となる色…
頂点法線の向きを変更しない場合の色は「赤=128、緑=128、青=255」になります。
これも青っぽい色です。

以上が、なぜノーマルマップは青いのかの解説です。
青色は127以下にならない理由
ノーマルマップの色の影響度は下記のような形で表されてます。
つまり、傾きが大きくなるとZの値が下がります。

そして、青が127以下になると時は…
それは「頂点法線の向きが面の形を超える」事を意味します。

実際の頂点にある頂点法線であれば… マイナス方向も対応します。
が、法線マップはマイナス方向には対応しません。

頂点法線がマイナス方向に行かないために、青色は255~128の範囲で動くようになってます。
以上が、青色は127以下にならない理由
おまけ:黄色いノーマルマップの作り方(青127以下)
たぶん、一生、使わないと思いますが…
理解を深めるために黄色いノーマルマップの作り方を解説します。

これは、Blenderのハイポリベイクでノーマルマップ作製を応用すれば、黄色いノーマルマップが作れます。

やる事の概要は下記。
①ポリゴン数が多いメッシュを下に敷く
②その凹凸を上のポリゴン数が少ないモデルを置く
➂「ポリゴン数が多いモデル」と「ポリゴン数が少ないモデル」凹凸の差のノーマルマップのテクスチャに記録する(ベイク)
要するに、下にあるポリゴンからノーマルマップを作ります。

↓詳細はこちらで解説。
普通のベイクだと、正しい青色のノーマルマップが生成できます。

そして、ベイク設定からBの方向を「-Z」に変更します。

これで、高さを表す数字が反転しました。
(Zの高さが「高いと青色=0」で「低いと青色=128」になりました)

この状態で、ノーマルベイクすると… 黄色いノーマルマップが生成できます。

あと、上のメッシュを突き抜ければ頂点法線の向きがマイナスになると思います。
が… Blenderのノーマルベイクにはセーフティ機能のようなモノが入っており
突き抜けたメッシュは処理されない挙動でした。(黄色いノーマルマップにならない)


理論的には、突き抜けで黄色いノーマルマップになりますが…
このあたりはご都合主義で修正されてます。
以上が、一生使わないであろう黄色いノーマルマップの作り方です。
ノーマルマップは頂点法線の影響に足される
まず3Dモデルには頂点法線があり、その方向で陰影の形が決まります。
↓はスムーズシェードを使った時の頂点法線の図です。

そして、頂点法線はマイナス方向まで行きます。

この上から、ノーマルマップを入れると…
頂点法線で決まった陰影の上にノーマルマップのピクセルで指定した頂点法線の情報が足されます。

そして、このような陰影ができます。

頂点法線が極端に傾いてる上に、極端に傾いた頂点法線を表すノーマルマップを組み合わせると…
理論的には、360°以上回ることがあります。

が…
そんな極端な頂点法線は基本的に発生しないので気にしなくて大丈夫です。
もし超えてもご都合主義的にそれっぽく見えます。(たぶん)
以上が、ノーマルマップは頂点法線の影響に足されるの説明です。
GLとDX形式の違いついて
ノーマルマップには「GL」と「DX」の2つの形式があります。
これは、頂点法線を制御する色情報の割り当て方が違います。

一般的なノーマルマップの「GL形式」は下図の様な対応です。

DXでは緑色のグラデーション、つまりY方向を表す色情報が逆になります。

そして… 特徴としては見た目の凹凸が逆方向になる所です。
GLは膨らんで、DXは凹んで見えます。

以上が、GLとDX形式の違いついての解説です。
GLとDX形式を変換する方法
考え方は、緑色の向きを反転するです。
一番実用的なのはトーンカーブで緑色だけ反転(左上から右下に進むカーブを作る)。
これで、変換できます。

実際に動かすと、こんな感じです。
GLとDXの凹凸の向きが逆になる=GLとDXが変換されたことが分かると思います。


↑ gif化した処理の関係でGLにマウスの跡が残ってますが…
ご愛敬という事で…。
違いの比較。
これが最も簡単な変換方法です。

また、先ほど行ったBlenderのノーマルベイクの場合は…

ベイク設定でYの値を「+Y」か「-Y」に設定する事で変換できます。

これで、ノーマルベイクの結果が変わります。


あと… 変換はしませんが用意したノーマルマップと
読み込めるマテリアルの設定が「GL」と「DX」が逆だった場合…
マテリアル側でY反転の機能があればGLとDXとして読み込んで使える事があります。
以上が、GLとDX形式を変換する方法です。
BlenderでGL↔DX変換する際の注意点
Blenderのシェーダーで使える、RGBカーブは上手く変換できません。
バグなのか、トーンカーブと微妙に処理が違うのか、緑反転が上手く動作しませんでした。

下図の様なマテリアルを設定。
(変化が分かりやすいようにカラーにノーマルマップを刺しました)

そして、RGBカーブの「G(緑)」を開き、左上から右下に向かうカーブを描画。
これで… G(緑)の入力が反転してDXに変わると思いますが…

なぜか、下図の様なミントグリーンみたいな色になりました。
謎です。

カラー分離 → カラーランププで反転なども試しましたがダメでした。


これに関しては、画像の方を編集した方が早いと思います。
以上が、BlenderでGL↔DX変換する際の注意点です。
UVの向きとノーマルマップの向きについて
ノーマルマップはUVの向きを計算に入れれません。

これが何を意味するかを説明するために、同じ長方形の平面ですが…
下図のような向きがバラバラのUVを用意しました。

そして、ノーマルマップの頂点法線の指定方法を思い出してください。
この頂点法線の指定方法は、画像全体に共通したルールで動きます。

つまり、UVの向きでノーマルマップで指定する頂点法線の向きが変わります。

なので、下図のような陰影の狂いが出ます。
この狂いは… 許容されるモノとなります。


というより、実情、直しようがないので許容するしかないです。
そもそもノーマルマップ自体疑似的な凹凸だから、それっぽく見えていればOKだよね。
という状態です。
ちゃんとした凹凸を作るなら「ディスプレイスメントマッピング」を使おうという話になってきます。
【ディスプレイスメントマッピングの概要】
・画像を使って、モデルに実際に凹凸を付ける技術
・サブディビジョンサーフェースなどで分割数を大量に増やして画像で "ポリゴンを" 編集して凹凸を作る
・ポリゴン数が大幅に増えるので、映画やアニメなどの映像作品向け
↓のように、ディスプレイスメントマッピングを使えば正しい陰影のディティールが作れます。
(ただし、非実用的なぐらい重い)
神経質な方は上下反転だけなら緑反転でできるので、UVの向きが上下で違う場合はそこだけ反転させるという使い方をするのモノ1つの手です。

よりこだわる方は…回転なども組み合わせるとより良くなるかもしれません。

が… 実際の3DモデルのUVはそもそも向きがめちゃくちゃです。
途中で面の方向が変わります。


このような立方体などのUVの面に合わせて
1つ1つノーマルマップの向きを調整するのは非現実的。
そして、やっても多分切れ目が綺麗にできず汚い見た目になる可能性が高いです。
また、UVは向きを意識して、なるべく正しい形に配置すると…
余白が大量にできるという問題があります。
なので、実際のモデルのUVは向きがバラバラだったりします。

このような点を考えると…
・この事を知った以上はなるべく正しい方向で揃える事を意識する
・上下が完全に反転した場合は「Y反転」を入れる位の調整ならok
・ただ、基本は諦める、受け入れる方向で進める
こちらが良いと思います。

ほかにも、VRChatモデルなどの場合は余計な処理を入れると
何も知らない一般ユーザーが使った際に混乱するので…
Y反転ぐらいはギリOKかもしれませんが、
基本はそのままで行くのをおすすめします。
以上が、UVの向きとノーマルマップの向きについての解説です。
まとめ
今回は、3DCGのノーマルマップの色の意味とGLとDXの違い+変換方法について紹介しました。
・3Dモデルのノーマルには「面法線(面の向き)」と「頂点法線(陰影の形)」の2つがある
・ノーマルマップは頂点法線をピクセルを使って制御できるようにしたモノ
・陰影の情報は、土台の頂点法線の上にノーマルマップの情報が加算されて入る
・GLとDX形式の違いはY(緑色)の数値の増え方
・Y(緑色)を反転すると、GLとDX形式を変換できる
・ノーマルマップはUVの向きを考慮できないので注意
・UVの向きによる、頂点法線の見え方の差は… 許容するしかない
また、他にもBlenderについて解説してます。

ぜひ、こちらもご覧ください。
コメント