インフィニットループ 技術ブログ

2023年05月17日 (水)

著者 : y-murakami

【Unity SRP】3Dテクスチャを利用したボリューメトリックフォグの実装

仙台で3Dゲームクライアント開発を行っております、にしきんです。

 ここ十年のハードウェア進化を身をもって体感する機会はモバイルゲーム上ではあまりないかもしれません。しかし数字で見てみると明らかで、例えば前世代の据え置きゲーム機であるPS4のGPU(GCN)はFP32について約1.84TFLOPS、そしてXbox OneのGPU(GCN)はFP32について約1.31TFLOPSですが、今日これは一部のスマホ向けGPUで、しっかりとFP32で達成できているようなものです。ぼちぼち前世代黎明期の描画トピックは抑えておくべきでしょう。

 ということで今回は、Unity向けの独自レンダリングパイプライン(SRP)で、3Dテクスチャを使ったボリューメトリックフォグの実装をしてみたので、その話を共有します。

ボリューメトリックフォグと3Dテクスチャ

 まず今回実装するボリューメトリックフォグは、ピクセルに侵入する輝度について、ランベルト・ベールの法則による減衰と、ミー散乱による直接光・間接光の散乱について経路に加わってくる分を、影を加味しつつ、数値積分で求めるようなものとします。

 この内容に3Dテクスチャが何故絡むのかが不思議に感じられるかもしれないですね。
ボリューメトリックフォグでは、各ピクセルに対応した光の入りについての散乱等を計算する必要があります。特に直接光の判定のために、シャドウサンプリングという位置的にコアレスされないサンプリングをフルスクリーンで、奥行方向に向けて積分的に複数回実行することになりますが、これはGPU的に嫌なわけですね。(他にもノイズ計算等を入れると演算能力が低めのGPUでは厳しくなってくるでしょうか。)

 二次元的なポストエフェクトとしてボリューメトリックフォグを実装する場合、そのパフォーマンスチューニングとして、低解像度に向けて処理を適応するといったことが考えられます。つまり、アップサンプリングして最終的な描画結果と合成するわけですが、その際にフォグがギザギザと漏れ出す又は欠落しているようなアーティファクトが見えてしまい、よろしくないのです。(緩和のためにぼかしの適応などもできますが、完全な対応は不可能ですし、パフォーマンス的にももったいないです。) ですが、ここでもしピクセルごとに計算するのではなく、あらかじめカメラ奥行方向に大気の情報を記録して、参照するような形にすれば、ギザギザ感はほぼなくなりますよね。3DテクスチャをUAVとして出力先に利用すればそれができますし、二次元的なポストエフェクトと同様に、任意の解像度で計算回数を設定もできます。

 もう一つ3Dテクスチャを利用する必要がある理由として、Deferredレンダリング以外の手法で描画される半透明などの対象は、3Dテクスチャを利用しないとフォグの適応が一貫しない形式になったり、非効率になったりするという話もあります。

 この記事を書いている最中に見つけたのですが、ここで実装しているボリューメトリックフォグの手法の概念的な説明を含め、フォグの歴史的な推移は例によってDigital Foundryさんが詳細にまとめてくださっていたので、ぜひこちらをご視聴ください。(英語)
二次元的な適応の場合の、解像度等の問題も説明されていますし、2014年を境に3Dテクスチャを利用しない手法は主流から外れたことが感じ取れるかなと思います。

実装結果


 さて、いきなりですが、これが実装した絵です。(Directional Light1つ(1lm/m^2)、ポストエフェクトなし)
パフォーマンスは、空間の計算結果を格納する3Dテクスチャ解像度が160x90x128という設定にて、Intel UHD 770 (約0.75TFLOPS,DDR5-4800)上での計算時間は2.2msほどでした。これは十分に低い性能環境での検証ですが、更にローミドルなモバイル端末では、160x90x64ですとか、128x72x128といった解像度の選択肢があると思います。計算時間はおおよそ半減しますが、あまり見た目に差は出ません。

 この結果から分かる通り、3Dテクスチャを利用する限りにおいて、ボリューメトリックフォグは見た目に対して非常に軽量な処理で、現代で実装を避ける理由はありません。

以下は実装のポイントです。

ランベルト・ベールの法則

光の進行の際の透過度として、
I/I0 = e^(-ax)
(入射光I0、透過光I、光が進む距離x、減衰係数a)
このようにTransmittanceが求まることになると思います。
伝統的な対数フォグをイメージしていると誤解するかもしれませんが、これは光の減衰であって、加算や自由な変調に使われるものではありません。フォグの白みを帯びた色を演出することにそのまま利用するとでたらめになることに留意しましょう。

間接光

 私の実装では、光が経路を進行する最中に、GIを表現する球面調和関数の係数を3Dテクスチャ的にアクセスするようにしています。
このようになんらかの方法でGIを加味することで、より表現力が増すでしょう。

最後に

 UnityはURPという標準描画システムを持っていますが、同時にSRPという、GPUへの命令構築を1から簡潔に行える基盤を公開しています。(そもそもURPはSRPの上で成り立っていますね。) エンジン側で標準に提供する描画だけでは、開発ごとのパフォーマンスや表現への要求を達成できないことが往々にありますから、そこをSRPで開発側の責任とするのはよい指針だと思います。

 最近はGPU側でコントロールできる対象が増えています。例えばSM6が登場したことで、GPUのハードウェアをより意識できるオペレーションが可能になりました。ハードウェア性能の単純な向上をそのまま享受するだけではなく、ユーザーの責任として、環境ごとに可能な限りの配慮を求められているような印象です。表現的な話としても、描画はゲームでプレイヤーさんが常に目にし続けるものですから、パイプラインで何をやっているのか・何が効率よくできるのかはよく掌握したいものです。詳細を知らないまま既成のものを使うですとか、自分たちで1から実装をしないといったケースは、Unityの世界でもこれからの時代、厳しくなってくるかもしれません。

 今回のトピックは古めの簡単な内容ですが、このようにILでは業務に関連して3Dゲーム開発の知見も日々飛び交っています。3Dグラフィクスは今社内でホットなトピックになっておりますので、また機会があれば記事を書きたいと思います。

興味を持った方は是非採用情報をご確認ください。

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!