[Unity] Box Projectionで屋内シーンの反射による映り込みの品質を向上する


これは 【unityプロ技】 Advent Calendar 2019の24日目の記事です。


Unity標準の ReflectionProbe のオプションにある Box Projection を設定することで、屋内シーンの反射の破綻を軽減して、映り込みの品質を向上する方法を紹介します。

百聞は一見に如かずということで、 Box ProjectionをON/OFF した動画を用意しました。

  • 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 になっていることを確認してください。

まとめ

Unity標準ReflectionProbeのBox Projectionと標準シェーダーだけで、屋内シーンの反射による映り込みを大きく改善できました。

映り込みが綺麗にできると、光沢感やリッチ感を表現できますし、設定もそこまで手間がかからないので、コストパフォーマンスが高いと思います!

みなさんも是非 Box Projection を使いこなしてみてください!