土屋つかさの技術ブログは今か無しか

土屋つかさが主にプログラミングについて語るブログです。

スクリプタブルレンダーパイプラインとはなにか

 Unity 2018.1から、シェーダーの内部処理に大胆なメスが入りまして、スクリプタブルレンダーパイプライン(SRP)という機能が実装されました。

スクリプタブルレンダーパイプラインの概要
https://blogs.unity3d.com/jp/2018/01/31/srp-overview/

 とはいえ、SRPはシェーダーアーキテクチャのかなり低レイヤの機構で、「SRPってつまりなんなの?」という質問に簡単に答えることができません。あと、普通にシェーダーアセットを使う分には触る必要が無い部分でもあります。

 ここでは、「レンダーパイプラインとはどういう物で、なぜSRPが必要なのか」について、簡単にまとめておきたいと思います。例によって土屋の認識違いになっている個所もあると思うので、コメントなどで補強していただけると助かります。

Unityのレンダーパイプライン

 シェーダーの役割は大まかに言えば「頂点情報をカラー値に変換する」ことなのですが、どのような手順でそれを実現するかは、実は決まっていません。「頂点シェーダー→ラスタライズ→フラグメントシェーダー」の順でデータが流れて行くのは決まっていますが、どのようなデータを流していくかはプログラムで制御できます。これを「レンダーパイプライン」と呼びます。

 「プログラムで制御できます」と軽く書いてしまいましたが、これは実は正確ではありません。Unityではこれまで、ユーザーがシェーダーを書く際、先にレンダリングパイプラインの種類を決め、決めた中でシェーダーコードを書く必要がありました。

 レンダリングパイプラインの種類は、GraphicsStettingsのRenderingPathで選択します。UnityではFoward Renderingが標準です。

フォワードレンダリング

 現在、コンシューマータイトルではFoward RenderingからDeferred Shadingに移行が進んでいます。

 フォワードレンダリングでは、全ての頂点/ピクセルに対して、全てのライトのシェーダー処理が最低一回ずつ実行されます。ライトが増えて行くと負荷に耐えられなくなるため、Unityでは頂点ごとに演算するライトの数を限定しています(それ以上のライトは近似値を使うことになります)。

 QualitySettings/Pixel Light Count

デファードシェーディング

 フォワードレンダリングは処理が明快で、長らく一般的なレンダリングパイプラインとして使われてきました(Unityでは現在でもフォワードレンダリングが標準です)。
 しかし、フォワードレンダリングでは、ライト1個ごとに1回Passが実行されるため、リアルな絵作りの為に大量のライトを配置すると、一気に処理が重くなってしまうという欠点があります。また、個々のライトが独立して処理されるため、ライト同士を連携させるのが難しいと考えられています。
 これらの問題に対処するために、現在広く仕様されているのがデファードシェーディング(遅延シェーディングとも言う)です。
 デファードシェーディングでは、GPUに比較的最近搭載されたMRT(マルチプルレンダーターゲット)という機能を使い、ライト反映前の頂点情報を、G-Bufferと呼ばれる複数のテクスチャにいったん保存します。
 全ての頂点の計算が終わった後で、G-Buffer上の情報を合成しながら、ライトの計算を加えてフレームバッファに書きこみます。ライトが何個あっても計算はスクリーンのピクセルごとに一回だけなので、フォワードレンダリングよりも計算負荷が軽くなることが期待できます。

フォワードレンダリング/デファードシェーディングの改良案

 デファードシェーディングは高機能ですが万能ではありません。実はフォワードレンダリングでは実現できていた半透明描画や、ハードウェアアンチエイリアスなど、基本的な機能が使えないという欠点があります。
 不足している個所についてはフォワードレンダリングで描画するのが一般的ですが、これだとアドバンテージが相殺されてしまいます。そこで、それらを解決するとして「フォワード+」や「ライトプリパス」などの新しいレンダリングパイプラインが提案されています。
 また、最近では「ボクセルコーントレーシング」など、現実的なリアルタイムレイトレーシング手法も提案されています。

SRPが必要になる理由

 とまあ、こんなふうにレンダリングパイプラインには複数の種類があり、どれを使うかは、トレンドだったり、各メーカーの工夫だったりで変わってきます。GPUの性能が向上したり、新たな機能が採用されたりすれば、それらを活用した、新たなレンダリングパイプラインが誕生することでしょう。

 しかし、これまでの(2017.xまでの)Unityでは、レンダリングパイプラインの選択はハードコーディングされていたため、基本的にはフォワードレンダリングとデファードシェーディングのどちらかを使用する事になり、新しいレンダリングパイプラインを気軽に導入することができませんでした。

 また、デファードシェーディングを利用する場合、G-Bufferの構造をカスタマイズするにはBuilt-in Shader Settingsを更新して専用のカスタムシェーダーを設計する必要があり、作業負荷を高めていました(これちゃんと調べてないので勘違いかも。UnityのデファードシェーディングではG-Bufferのフォーマットって固定なんでしょうか?)

 というわけで非常に長々と書いてきたわけですが、この状況を改善するために採用されたのが、C#でレンダリングパイプラインの構造を定義できるようになったスクリプタブルレンダーパイプライン(SRP)なのです。

 SRP使えば、エンジニアは自分が採用したいレンダリングパイプラインを、Unityで導入することができるようになります(SRPの表現力が許す範囲での話ではあるけども)。SRPでは、レンダリングパイプラインの低レイヤ機構をブロック化してC++で実装していて、ユーザーはそれらを選択することで、必要とするレンダリングパイプラインを構築できます。

 SRPで構築されるのはレンダリングパイプラインであり、その上で実行されるシェーダーコードは別途実装することになります。SRPがどこまでの拡張性を持っているのか土屋は把握していませんが(まだフォワードレンダリングでやることが山ほど残っているので、SRPに到達するのは何年か先だと思っていますw)、Unityの表現力が広がるなら、喜ばしい事ですね。

補足

書き終わってから下記サイトとほぼ同じことを書いてたことに気づき茫然

Unity 2018.1 より提供される Scriptable Render Pipeline について調べてみた
http://tips.hecomi.com/entry/2018/02/19/000846

[asin:4862464130:detail]