Unreal EngineのEmissive Colorにて擬似的に指向性を持たせる方法

 本投稿は,Unreal Engine(UE)のEmissive Colorに対し,見る角度によってその強さを制御し,その自発光にあたかも指向性があるかのように見せる方法に関する投稿です.
 UEでは,ライトを直視しても眩しく見えず.僕はその解決法としてEmissive Colorを用いています.しかし,Emissive Colorは全方位に自発光するため,例えばLEDのような指向性をもつ光源を模倣することはできません.そこで,見る角度によってEmissive Colorの強さを制御し,擬似的な指向性を加えます.
 使用しているUEのバージョンは4.25.4です.

目次

  1. 結果
  2. 概念
  3. 実装手法

1. 結果

 Emissive Colorのデフォルトと本手法の比較した様子を図1に動画で示します.
 デフォルトでは,自発光面に対し角度を付けて見ても,見えている自発光面にはブルームが生じます.これに対し,本投稿で紹介する手法にて疑似指向性を実装すると,角度を付けて見るとブルームは生じなくなります.

図1 Emissive Colorのデフォルトと本手法の比較

2. 概念

 Emissive Colorのマテリアルが適応されているピクセルの法線ベクトルと, Emissive Colorの マテリアルが適応されているピクセル座標とカメラ座標によるベクトルとの,内積を使用します(図2).そして,内積から逆三角関数で導出した角度に応じて, Emissive Colorの強さを制御します.

図2 2つのベクトルから内積を求めるイメージ

 内積から逆三角関数で角度を導出した場合,カメラの位置ごとにの結果は図3のようになります.
 そして,角度として扱っていた値を,Emissive ColorにおけるRGBへの乗数に向けて0~1の値に変換します(図4).

図3 カメラの位置ごとにおける導出される角度
図4 角度から0~1の値へ変換しEmissive Colorの自発光の強さを制御するイメージ(負の数は別途処理する)

3. 実装手法

 マテリアル エディタの全体を図5に示します(図5~図9は,クリックすると拡大表示されます).
 図5中の,コメントごとに区切った各内容について順に説明します.

図5 マテリアルエディタの全体像

  ピクセルおよびカメラの位置ベクトルと,法線ベクトルとの内積の導出について説明します(図6).
 「絶対ワールド位置」ノードは,各ピクセルの位置座標をワールド座標系で取得できます.取得できる位置は,マテリアルを適用されたメッシュの面ではなく,ピクセル単位です.
 「Camera Positon」ノードは,カメラの位置座標をワールド座標系で取得できます.
 そして「Subtract」ノードは,AピンとBピンに入力された値の差分を求めます.これによって,図2にてオレンジ色で示したベクトルを作成します.ベクトルではA引くBと,B引くAとで,出力されるベクトルの向きが異なるため,順序に気をつけます.
 「PixelNormalWS」ノードは,ピクセルの法線ベクトルを取得できます.
 「Dot」ノードは,AピンとBピンに入力したベクトルの内積を出力します.ここで,前述で求めたピクセルの法線ベクトルは単位ベクトルであり,大きさは1です.しかしピクセルとカメラの各位置座標から求めたベクトルは2点間を結ぶ距離に応じた大きさを持っています.そこで「Normalize」ノードによって,ベクトルの大きさを1にしたうえで,内積を求めています.

図6  ピクセルおよびカメラの位置ベクトルと法線ベクトルとの内積

 内積の結果は,用いた2つのベクトルの長さとcosの積になります.ここでは2つのベクトルの大きさは互いに1であるため,単純にcosの値です.そして逆三角関数で角度を求め,さらに扱いやすい度数法(degree)に変換します(図7)
 まず「Arccosine」ノードによって,内積の結果を角度に変換します.
 しかし,UEの「Arccosine」ノードは弧度法による角度を出力します.度数法の方が個人的には何かと扱いやすいため,変換します.弧度法から度数法に変換するには,定数180で掛け,さらに円周率で割ると求められます.
 「Multiply」ノードは積,「Divide」ノードは商を求めます.
 ここまでの操作によって,図3に示した状態になります.

図7 内積による角度を弧度法から度数法(degree)へ変換

 Emissive Colorにおける自発光の強さとして扱うために,0~1の値にしてゆきます(図8).
 定数90で引き,さらに定数90で割ると,図4の状態になります.
 この段階では,図4に示すように,自発光ピクセルの真横よりも背後にカメラを移動させると,負の値になります.

図8 Emissive Color向けに0度~90度から0~1へ値を範囲を変換

 図4や図8の説明では,数値が取り得る範囲の下限値に対し,0と表現しました.しかしEmissive Colorは,元となる色(RGB値)に対し,数値を乗算させることで自発光させます.そのため,乗算する数値が0の場合,乗算結果は0となりEmissive Colorを適応しているピクセルは黒色になってしまいます.
 つまりこれでは,例えば図1の自発光部分に対し角度をつけて見た場合,「マゼンタ色に自発光しているが眩しくは見えない」のではなく「マゼンタ色から黒色(=自発光の消灯)へ変化してゆく」ようになってしまいます.
 そこで下限値を0より大きな値にする必要があります.そしてEmissive Colorの自発光する下限値はどうやら0.01のようなので,「if」ノードを用いて0以下の場合は0.01とします(図9).
 これにより,見かけ上ではブルームが減衰しあたかも指向性の範囲外から自発光面を見ているようにしつつ,色は黒色にならないようにできます.
 下限値を0,01で固定することにより,乗算結果が0となり黒色になる問題の回避と共に,負の値にもならないようにしています.Emissive Colorは負の値を取らないため,(負の値を入力しても何かしらの挙動はするでしょうが)プログラム上の管理として,負の値を入力してしまわないようにもしています.

図9 下限値の設定