これは 【unityプロ技】 Advent Calendar 2019の24日目の記事です。
Unity標準の ReflectionProbe のオプションにある Box Projection を設定することで、屋内シーンの反射の破綻を軽減して、映り込みの品質を向上する方法を紹介します。
百聞は一見に如かずということで、 Box ProjectionをON/OFF した動画を用意しました。
Box Projectionで屋内シーンの反射による映り込みの品質を向上する
— がむ (@gam0022) December 23, 2019
という記事を書きました!
▶️https://t.co/gz0CpeizHb
カメラの位置を考慮して、Cubemapの映り込みの位置を正しく補正する手法です。
設定が簡単なわりに、劇的に見た目のリッチ感UPできるのでオススメです!#Unity3D #shader #HLSL pic.twitter.com/o75TDgPBL2
- Box Projection: OFF では、カメラの位置が無視されて、家具などの映り込みの位置がずれてしまっています。
- Box Projection: ON では、カメラの位置が正しく考慮されて、家具などの映り込みの位置が正しくなっています!
環境
- 2018.4.13f1 (LTS)
- Build-in Rendering Pipeline および LWRP(Light Weight Render Pipeline)
Box Projectionとは、どんな技術なのか?
公式マニュアルにもBox Projectionの解説はあるのですが、ちょっと初心者には理解しずらかったので、簡単に解説をします。
端的に言うと、Cubemap(ReflectionProbe)のテクスチャをサンプリングするための反射ベクトルの向きを、カメラの位置と部屋の広さを定義するBoundingboxを考慮して補正する手法です。
普通のCubemapとの違いは、反射ベクトルの計算の違いだけなので、GPUの処理負荷もそれほど高くはありません。
反射はしていますが、レイトレをするわけではないので、RTXも不要です!
Parallax-Corrected Cubemapの解説
※Box Projectionの理論に興味がない人は、この節はスキップして本題から読んでください。
Unity標準のStandardShaderでBox Projectionは実装済みなので、理論は理解しなくても使うことはできます。
UnityではBox Projectionという名称ですが、Parallax-Corrected Cubemap とも呼ばれる手法です。
Parallax-Corrected Cubemapの具体的な理論と実装については、このスライドに非常に詳しく解説してあります。
この図は上記のスライドから引用しました。
この図を用いて簡単にParallax-Corrected Cubemapの解説を行います。基本的な考え方はそれほど難しくはありません。
まず各記号の説明をします。
- $\vec{V}$: カメラのビューベクトル
- $C$: ReflectionProbeの位置
- $\vec{N}$: 法線
- $\vec{R}$: 反射ベクトル(補正前)
- $\vec{R'}$: 反射ベクトル(補正後)
- $P$: $\vec{R}$と壁の交差点
$\vec{R}$ 方向をサンプリングすれば、 普通のCubemap ですが、
$\vec{R'}$ 方向をサンプリングすると視差が考慮された Parallax-Corrected Cubemap になります。
そして、図が示すように $\vec{R}$ を補正した $\vec{R'}$ は
\vec{R'} = P - C
によって計算できます。
$C$ は既知なので、 $\vec{R}$と壁の交差点である $P$ を計算すれば、$\vec{R'}$ が求まります。
ベクトルとAABBの交差判定のアルゴリズムを用いれば、$P$ は計算ができます。
HLSLによる Parallax-Corrected Cubemap の反射ベクトル $\vec{R'}$ を求める実装例です(MITライセンス)。
// Parallax-Corrected Cubemap
// https://seblagarde.files.wordpress.com/2012/08/parallax_corrected_cubemap-siggraph2012.pdf
float3 calcParallaxCorrectedCubemapReflect(float3 worldPos, float3 worldReflect, float3 cubemapPos, float3 boxMin, float3 boxMax)
{
// AABB と 反射ベクトル の交差点 worldIntersectPos を計算します
float3 firstPlaneIntersect = (boxMax - worldPos) / worldReflect;
float3 secondPlaneIntersect = (boxMin - worldPos) / worldReflect;
float3 furthestPlane = max(firstPlaneIntersect, secondPlaneIntersect);
float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);
float3 worldIntersectPos = worldPos + worldReflect * dist;
// Cubemapから交差点に向かうベクトルが ParallaxCorrected された反射ベクトルになります
return worldIntersectPos - cubemapPos;
}
壁の近似はAABBに限定しなくても交差判定ができれば何でも良いのですが、計算コストと品質のバランスを考慮してAABBが一般的に用いられるようです。
Box Projectionに対応した自作シェーダーの紹介
当初は、Parallax-Corrected Cubemapに対応したUnityシェーダーを実装するという記事を執筆する予定だったのですが、Unity標準のStandardShaderで実装済みであることを記事用のシェーダーの実装が完成してから知りました
供養のために、Box ProjectionとLightmapに対応した自作シェーダーのリンクを紹介します。
[本題] Box Projectionの設定
それでは、ここからが本題です!
Box Projectionの設定をして、最初の動画のようなシーンを作る方法をチュートリアル形式で紹介していきます!
0: 屋内のアセットを用意する
まずは屋内シーンのアセットを用意します。
今回はUnityアセットストアから Pack Gesta Furniture #1 をダウンロードして使わせていただきました
1-1: ReflectionProbeを配置する
まずは ReflectionProbe を配置します。
普通は部屋の中心あたりに適当に配置すればOKです。
今回は映り込みの品質を向上するために、ちょっとカメラの位置に寄せました。
1-2: ReflectionProbe の BoxSize と BoxOffset を設定する
部屋の広さにぴったり合うように、BoxSize と BoxOffset を設定します。
Wireframeモードにしたほうが、作業がしやすいかもしれません。
また、BoxSize と BoxOffset は ReflectionProbe の影響範囲の指定も兼ねているので、床のMeshに少しオーバーラップするようにサイズを調整する必要があります。
オーバーラップしないと、ReflectionProbe の影響範囲外となってしまいます。
1-3: Box Projection等の設定とBake
ReflectionProbeの設定を変更します。
- Box Projection: ON
- Type: Baked
- その他はお好みでOK
設定が完了したら、 [Bake] ボタンを押して、Cubemapのテクスチャを生成します。
2: 床のマテリアルの設定
床のマテリアルを反射が見えやすく調整します。
- StandardShader を設定(ReflectionProbeに対応していれば何でも良い)
- Metalic: お好みですが、フローリングの床感を出すなら 0.2 前後あたりがいい感じです
- Smoothness: 1.0 に近づくほど反射がハッキリ見えます
- Normal Map: なしにするか、影響度を 0.01 など小さな値にするほど反射がハッキリ見えます
以上でいい感じに反射するようになると思います。
まるで年末の大掃除でピカピカに磨き上げられた綺麗なフローリング床みたいですね!
トラブルシューティング
記事の執筆中に遭遇したトラブルをまとめます。
ReflectionProbe に映らないオブジェクトがある
- 対象の GameObject は static になっていますか?
- ReflectionProbe の CullingMask は Everything になっていますか?
再生したら反射が消えた
staticバッチングが原因の可能性があります。床のMeshだけ static を OFF にしましょう。
Box Projection が有効にならない
Project Settings > Graphics > Reflection Probes Box Projection
が ON になっていることを確認してください。
URPでBox Projection が有効にならない
URP Assetsに設定項目が移動しました。
まとめ
Unity標準ReflectionProbeのBox Projectionと標準シェーダーだけで、屋内シーンの反射による映り込みを大きく改善できました。
映り込みが綺麗にできると、光沢感やリッチ感を表現できますし、設定もそこまで手間がかからないので、コストパフォーマンスが高いと思います!
みなさんも是非 Box Projection を使いこなしてみてください!