"The Last of Us" のライトマップやソフトシャドウについて(SIGGRAPH 2013)

概要

  • この記事では "The Last of Us"で使われた以下のテクニックについて簡単に説明します.
    • Dominant Light Axis 方式のライトマップ(環境光のイラディアンス, ライティングの主方向, その主方向のライティングの強度を持つ)
    • ライトマップの継ぎ目の緩和方法
    • キャラクターを多数の球で近似することでのリアルタイムのソフトシャドウ

目的

  • The Last of us は 細菌で汚染されて人が大量に死んでいる破滅的な世界がテーマでした.
  • また電気や人工的な光源がないという世界でもあるので, 直接光ではなくぼんやりとしたディフューズ間接光やスペキュラ間接光, ソフトシャドウによって世界を描きたい, というアートの方向性がありました.
  • 下の 3 つの図はコンセプトアートで, 直接光が当たらない暗がりの中で柔らかいライティングや微妙な陰影が表現されています.



ライトマップについて

  • ハイクオリティのディフューズ間接光やスペキュラ間接光をリアルタイムに計算するのは難しいので, この作品ではライトマップを使うことにしました.
  • ライトマップとはランタイムではなく, オフラインでライティングを事前に計算した結果をテクスチャに書き込んで, それをゲームのモデルに貼り付けてランタイムで参照する方法です.
  • 長所は高品質なライティング結果をランタイムで参照できることや, ランタイムではライトマップを参照するだけなので GPU の処理負荷が軽いことです.
  • 短所はライトマップのテクスチャデータが大きくなりがちなこと, 動的なライティングやモデル形状の変化があった場合にライトマップが破綻すること, ライトマップ用の UV の生成やライトマップの作り直しが何度も発生するのでアーティストのワークフローが複雑になることなどがあります.
  • よく使われるライトマップの方式は環境光としてのイラディアンス(大雑把に言うと, ある点から半球方向に入る光の和)を求めて π で除算したものを テクスチャに保存する方式です.
  • しかし, この方式だとライトの方向に関する情報がないのでライトマップに対して法線マップを適用することができなくなります.
  • そこでこの作品では後述する Dominant Light Axis 方式のライトマップを採用して, 方向に関する情報を持てるようにしています. (下図のスライド参照)
  • 他に方向を考慮したライトマップについては Radiosity Normal Map (Half Life2, Mirror's Edge)や SH LightMap(Halo3, Halo4, Agni's Philosophy ) などがあります.
  • ただ方向を考慮したライトマップを導入した場合はテクスチャ枚数が増えるという問題があります.
  • しかし, 直接光をライトマップに焼かない場合はライトマップが低周波になる傾向があるので, そのような場合はライトマップの解像度を下げて利用するという方法もあります.


The Last of us の Dominant Light Axis 方式のライトマップ

  • この作品ではライトマップに後で部分的に法線マップを適用できるようにするために, Dominant Light Axis のライトマップを使っています.
  • このライトマップでは 通常のライトマップの「環境光のイラディアンス(RGB) 」に加えて, 「ライティングが強い 1 方向である主方向(RGB)とその強度(RGB)」を持つ形式になっています.(下図のスライド参照)

  • Dominant Light Axis 方式のライトマップは法線マップが後で適用できると言いましたが, 実際に法線マップが適用できるものは方向の成分を持っている「ライティングの主方向(RGB)とその強さ(RGB)」のところだけです.
  • またこれには主に焼き付けているディフューズライトの成分が入ると思います.
  • 一方で「環境光のイラディアンス(RGB) 」については方向の情報がないので, 従来どおりにライティング成分をただ加算するものとなります.
  • 実際にこの Dominant Light Axis 方式のライトマップを使ってディフューズライティングの計算をするときは以下のような式になります.
  • 各記号の意味については以下のようになります.
    • I : ライトマップによるディフューズライティングの結果
    • c_amb : ライトマップ中の環境光のイラディアンス
    • c_dir : ライトマップ中のライティングの主方向(RGB)
    • N : 頂点法線や法線マップの法線
    • L : ライトマップ中のライティングの主方向の強度 (RGB)

  • またこの作品では, マテリアルがグロッシーな場合はこの「ライティングの主方向(RGB)とその強さ(RGB)」を使って, 偽の白い(?)スペキュラハイライトを発生させていたようです.
  • 余談ですが, 実際にこの Dominant Light Axis 方式のライトマップを作る際には, Naughty Dog の自前のグローバルイルミネーションのベイクツールを使って SH 表現のライトマップを作ってから, この Dominant Light Axis 方式のライトマップを作っているそうです.(下図のスライド参照)

  • 補足説明として, 今回の The Last of Us で使われた Dominant Light Axis 方式のライトマップに関係するスライドを貼っておきます.
  • 下図は Dominant Light Axis の説明のスライドで, Dominant Light Axis が以下の 3 つの成分から構成されることを説明しています.
    • ライトマップ中の環境光のイラディアンス(RGB)
    • ライトマップ中のライティングの主方向(RGB)
    • ライトマップ中のライティングの主方向の強度(RGB)


  • 次にスライドに載っていた長所と短所について簡単に紹介しておきます.
    • 長所にはマテリアルのタンジェント空間に依存しないこと(一方で Radiosity Normal Map は依存します)や, 「ライティングの主方向(RGB)とその強さ(RGB)」がディレクショナルライトに相当するのでわかりやすいと述べられています.
    • 一方で短所には「ライティングの主方向」が不連続である領域を補間した場合, 方向があちこち散らばってしまうことや, 方向については 1 方向の情報に限られてしまうことが挙げられています.
  • The Last of Us でこの不連続性による不具合がシャドウの境界で発生していたので, ローパスフィルタをかけてぼかすことで解決していました.



ライトマップの継ぎ目への対策

  • 3D 空間的に繋がっているメッシュでも, ライトマップの UV 空間上で離れているときに両者のエッジの色が一致しないと, 下図のようにライトマップの継ぎ目に色の違いが出てしまう ライトマップの継ぎ目( Seam )が発生します.

  • UV レイアウトを直すプログラムを書くという方法もありますが, 今回はその方法は実装コストが高いので使わないようにしています.
  • その代わり, The Last of Us では下図ようにライトマップの継ぎ目のピクセルの色を調整して目立たないようにしています.

  • ライトマップの継ぎ目のピクセルの色を調整するために, ライトマップの UV 空間が離れているエッジ部分について 1 ピクセルあたり 3 点ずつサンプル点を作っておきます.
  • このサンプル点について例えば下図の右の緑色の UV の点 Ci0 の色を計算する場合にはバイリニアフィルタを考慮した場合には 4 個のテクセル t に対して重みをかけてものの和になります.
  • このサンプル点について 緑色の UV の点 Ci0 と, 紫色の UV の点 Ci1 が同じ色になれば継ぎ目の問題が緩和されるので, その差分の 2 乗を下図の右のように Ei とします.
  • これをライトマップ全体の継ぎ目に対して計算したものを下図左のように E_total とした場合, この E_total が最小になるようにライトマップの継ぎ目を調整すれば, 全体として継ぎ目が目立ちにくくなることになります.


  • そしてライトマップ全体の継ぎ目のエラー値 E_total を最小化するために, ライトマップの継ぎ目のテクスチャカラーを変数としてこの連立線形の方程式を解くことで, 継ぎ目を目立たなくすることができます.
  • 今回は自分らでこのソルバーを実装することはせずに, Eigen libraryを使っていました.

キャラクターのソフトシャドウ

  • 次にキャラクターのソフトシャドウについてです.
  • キャラクターにシャドウがないと地形に対する接地感がなくて不自然に浮いて見えてしまうので, よくキャラクターが動的に落とすシャドウのためにデプスシャドウが使われます.
  • しかし, この方法だと下図のようにキャラクターが既にデプスシャドウの中にいる場合はシャドウが落ちないので, キャラクターが浮いて見えてしまう問題があります.


  • Uncharted 2 ではデプスシャドウの中でもキャラクターの接地感を出すために SSAO を使っていました.
  • 下図は Uncharted2 の SSAO が無効のときの図です. キャラクターがデプスシャドウの中にいるので接地感がなくなっています.

  • 下図は Uncharted2 で SSAO が有効な場合で, キャラクターの足元にアンビエントオクルージョンが入って接地感が生じています.

  • しかし, SSAO だとシャドウに指向性がないので 少しシャドウとしての存在感が弱いです.
  • そこで, The Last of us ではシャドウの中においてもぼんやりと指向性がある影が落ちるように, 下図のようなキャラクターが落とすソフトシャドウを実現しています.


  • どうやって実現しているかについては, 簡潔に言うとキャラクターの体を下図のように球や楕円体で近似して, 影が落ちる先においてそれらの遮蔽率を計算しています.

  • この遮蔽率の計算は以下の 2 種類のものを行っています.
  • (A) 半球に対する球の遮蔽
    • 1 つ目は下図の左側のように, 遮蔽率を計算する点を中心とした半球に対して球がどれぐらい遮蔽しているかを計算します.
    • この結果はアンビエントオクルージョン的なものになり, ライトマップ中の環境光のイラディアンス(RGB)に対して乗算して利用します.
  • (B) 主方向ライトに対する球の遮蔽
    • 2 つ目は下図の右側のように, 遮蔽率を計算する点を中心とした半球において(ライトマップの計算によって求めた)主方向ライトに対する球の遮蔽です.
    • この結果はライトマップ中のライティングの主方向や強度に対して乗算して, ソフトシャドウっぽい表現をするのに利用します.
    • 主方向ライトの方向についてはライトマップの値を利用すると書きましたが, 場合によっては主方向を持たない従来のライトマップを使っていることがあるので, そういうときはディレクショナルライトや法線の方向を利用しているそうです.

  • 上のような遮蔽の計算をキャラクターを球で近似したモデルのそれぞれの球ごとで遮蔽を計算して, その結果をライトマップに対して乗算します.
  • 但し, こういうように計算すると複数の球の遮蔽が重なっている箇所が本来の値よりも暗くなってしまいますが, 今回はその結果が目立たないので特に対策はしていないそうです.

  • (A)の半球に対する球の遮蔽については計算がシンプルで数式で求めることができます. (計算としては球を半球に対して射影して求めた立体角に cosθを乗算したものを, 微小立体角に対して半球積分したもの)
  • しかし (B)の主方向ライトに対する球の遮蔽の計算については手が込んでいる(Ambient Apeture Lightingの資料)のでランタイムで計算するのを避けるために, 事前にモンテカルロ法で計算しておいた結果を下図のような 2D テクスチャにオフラインで焼きこんでおきます.
  • そして, ランタイム時にφ(球を半球に射影したときのコーンの頂角)とθ(主方向ライトの軸と球の中心を通る軸の間の角度)を使って, この 2Dテクスチャを参照して利用します.
  • さらに, 遮蔽計算する際の「ライトの主方向のコーンの頂角の角度」を調整したい場合にはパラメータとしてもう 1 次元必要なので, 結果は 3D テクスチャになります.

  • 上で説明した遮蔽計算する際の「ライトの主方向のコーンの頂角の角度」についてはアーティストが調整できるパラメータになっていて, 下図の左から 60°, 30°, 10 °と探索範囲を絞っていくとソフトシャドウがよりシャープに変化します.


  • 以下ではより具体的な処理の手順について説明していきます.
    • 0. 最初に GPU でソフトシャドウを受ける地形などのライトの主方向のベクトルの結果をテクスチャバッファに書き出しておきます.(下図の緑色のバッファ)

    • 1. SPU で画面を小さいタイルに分割して, 遮蔽の各球がどのタイルに対してソフトシャドウの影響を与えるか ? を求めます. ( 遮蔽の各球にはそれがソフトシャドウとして他に影響を与える範囲の球が設定されています. ) ここでの計算はタイルベースのライトのカリングと似ているのですが, The Last of us ではより精度が高い交差判定を行います. 具体的にはタイルごとに視点座標系でのそのバウンディングボックスを計算し, それが遮蔽の各球の影響範囲の球と交差するか ? の判定を 2D 的にではなく, 3D の視点座標系で行います.

    • 2. タイルごとに, それに遮蔽の影響を与える各遮蔽の球のリストが求まったので, SPU でそれらの球に対して (A) 半球に対する球の遮蔽を R 成分, (B) 主方向ライトに対する球の遮蔽を G 成分として, 遮蔽用のテクスチャ(下図の黄色のバッファ)に書き出します.

    • 3. 最後に下図のように GPU のメインのレンダリングのパスで, 遮蔽のテクスチャを参照して「ライトマップ中の環境光のイラディアンス」と「ライトマップ中のライティングの主方向の強度」を減衰させた最終結果を求めます.

  • 処理負荷については 4-5 名のキャラクターがいる場合に 6 個の SPU でこの処理を行うと 2msec で GPU が遮蔽用のテクスチャを参照する準備ができるとのことです.
  • あと最適化のために, 球の遮蔽の一連の計算は 1/4 の解像度で行います.

  • また遮蔽の計算を高速化するために複数の球の代わりに球を 1 軸方向にだけスケールした楕円体も遮蔽の計算に使えるようにしています.
  • この楕円体とコーンの遮蔽計算をするときにはコーンをスケールして楕円体の座標系に変換してから行い, その後は球とコーンの遮蔽計算を行います.
  • 但し, コーンの角度まで楕円体の座標系に変換すると計算が重くなるので, 近似としてコーンの角度はそのまま扱っているようです.

  • キャラクターのソフトシャドウについては今回の多数の球による遮蔽計算で行っています.
  • 一方で, 今回の球による近似を使った場合には正確なソフトシャドウを求めるためには多数の球が必要になってしまい処理が重くなるオブジェクトの場合があります.
  • その場合には事前に遮蔽されていないコーンの頂角の大きさと, 遮蔽されていない方向の平均(いわゆる Bent Normal)を 3d volume texture として事前に計算して保存しておき, ランタイムの遮蔽計算時には 3d volume texture に保存されたコーンの情報を利用して行います.

  • コーンの遮蔽情報を事前に 3d volume texture として求めておくことのイメージ図は下図のようになり, この内容については GDC2012 の発表であった 3d volume texture を使った AO Fields ( "Ambient Occlusion Fields and Decals in Infamous2" ) とほぼ同じだと思います.