simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:レールに沿ったカメラを表現するCinemachine

前回、初めて Cinemachine を触ってみましたが、サクッと三人称視点のフリーカメラの動きを実現できました。
simplestar-tech.hatenablog.com

Cinemachine の見どころは多く、まだまだその機能を紹介しきれていません。
カメラワークのイメージと、それを Cinemachine で実装する手順の間のメンタルモデルの定着を目指して
今回はレールに沿って動くカメラの動きを実現してみたいと思います。

■Cinemachine の導入
Unity 2018.3 beta を使っていますが、それでもまだ Cinemachine はプレビュー版のパッケージの様子
まずは Windos > Package Manager とメニューを選んで、最初は All packages でも Cinemachine は見つけられませんが
Show preview packages を Advanced メニューから選んでインストールします。

インストールした Cinemachine のバージョンは 2.2.7 でした。

■Main Camera に Brain を追加してカメラの主導権を Cinemachine に譲渡
ゲームの絵を作り出すのは、変わらず Main Camera オブジェクトです。
その Main Camera オブジェクトを制御するわけですから、そこに Cinemachine Brain コンポーネントをアタッチします。
これで Main Camera を手動で編集できなくなりました。(強制的に Cinemachine が制御します)

■カメラの動きを決めるレールを敷く
Cinemachine Smooth Path というスクリプトコンポーネントが使えるようになっていますので、空のオブジェクトに設定します。
f:id:simplestar_tech:20180918084124j:plain

Waypoints の + ボタンをポチポチ押せば、こうしたレールが表示されるので適当な軌跡を描きます。

Cinemachine Smooth Path は他にも使い道ありそうだなぁって思ったのが、次のインタフェースを提供してくれる点です。
具体的には 0~1の値を渡して軌跡上の点の位置を返してくれるというもの

    [SerializeField]
    private Cinemachine.CinemachineSmoothPath path;

    void Update()
    {
        transform.position = path.EvaluatePositionAtUnit(0.5f, Cinemachine.CinemachinePathBase.PositionUnits.Normalized);
    }

手付けのアニメーションには必須だと思う機能なのですよ!
このオブジェクトはここでこういう風に動いて…など
レールだけにちょっと脱線しました。

■カメラの動きを強制する Virtual Camera に Path を指定する
今回は Path を指定してその Path に沿って Track & Dolly というレールに沿った動きを実現するので
インスペクターにある Body と Aim をいじります。
Body のドロップダウンメニューから Track & Dolly を選択し Path に先ほど作成した Cinemachine Smooth Path をアタッチしたオブジェクトを設定します。
Auto Dolly の Enabled にチェックを入れれば、レールに沿ってカメラが対象を追従します。(LookAt、Follow に対象となるオブジェクトを設定する必要あり)
さらに Aim にて Composer を選び、適当に Dead Zone などを設定して、映画監督のように Shot を指示します。

シーン内にこの Virtual Camera があれば、カメラは勝手にレールの上を Track & Dolly しながら撮影してくれるはずです。
次のように

f:id:simplestar_tech:20180918085640g:plain

■まとめ
レールに沿ったカメラを表現するCinemachine の機能を実践しました。
ゲームのデモムービーや、ゲーム内でのカットシーン(ゲームオブジェクトを使ったリアルタイムレンダリングムービー)の作成で活躍する技法です。
Unity のデザイナー向け機能ですが、プログラマーも覚えて損はないと思いますよ!

■参考資料
ここで紹介する 4分の動画を見れば、この記事で書いた内容がすべて映像で学習できます。最高の資料でした、ありがとうございました。
unity3d.com


■次回予告
次は頂点色を設定するメッシュ作成と頂点カラーを表示するシェーダーについて書いていきたいです。

Unity:CinemachineでTPSカメラ制御してみる

Chinemachine (シネマシーン)とは、映画のカメラワークを強力にサポートするツールセットの名称です。
映画監督ばりに、絵コンテ通りにカメラを動かしたい、そんなときに役立つ公式アセットです。(あるときから公式になった人気ツール?なのかな)

とりあえず映像に関するツールセットなので、映像でどういった機能を私たちに提供してくれるのか確認してみましょう。
unity3d.com

動画から得られた知見:
1.Package Manager を開いて Preview 版の Chinemachine をインストールする
2.知的な Main Camera にするため Main Camera オブジェクトに Chinemachine Brain コンポーネントを追加
3.Timeline Editor に2.のコンポーネント追加済みのオブジェクトをドラッグ&ドロップして、トラックの種類に Cinemachine を選択して Cinemachine Track を追加
4.Timeline の編集とは、オブジェクトに紐づくトラックを並列に追加し、そのうちの一つのトラックの中にクリップを順番に配置することだった
5.トラックの中に Cinemachine Shot Clip を作成、その Shot に関するカメラ位置姿勢と対象の追従設定などを記入する、このShotに関する設定を「 Virtual Camera 」と呼ぶことにしている
6.カメラ画像上の領域にて、対象を領域内にとらえている間はカメラは死んでいるかのように動かないとするカメラワークがある、この時のカメラの画像上の領域を「Dead Zone」と呼ぶ
7.Dead Zone の周りの領域を「Soft Zone」その外側は特に呼ぶ名前はない
8.トラックにはいくつでも Shot Clip を追加できるし、Clip 間でカメラワークを補間したり、ワープしたりをGUI操作で簡単に操作できます。

わずか 5 分で Timeline の編集の概念と、Cinemachine の基本機能と Timeline でのカメラワークの操作まで完璧に示していました。
とても良い動画だと思いました。

ここまでの基本的な知見を持ったうえで次のページを読んでみました。
gametukurikata.com

得られた知見:
1.Main Camera オブジェクトに Chinemachine Brain コンポーネントを追加、シーン内に「Virtual Camera」を追加すると、そのVirtual Camera の設定に従って Main Camera の動きが強制される
2.Virtual Camera の設定に Follow と Look At があるので、ここに移動キャラクターを設定すれば TPS (三人称視点)のカメラの動きに近づく(キャラクターを追いかけるカメラの動き)
3.Free Look タイプの Virtual Camera も用意されている、次の図に示すような三つの円で設定される楕円体の表面に沿って対象を中心に移動するフリーカメラの操作を提供してくれる
f:id:simplestar_tech:20180917164001j:plain
まさに TPS (三人称視点)のカメラの動きじゃないですか!これ

サクッとキャラクターを用意
f:id:simplestar_tech:20180917172029j:plain

Free Look タイプの Virutal Camera をシーンに配置してゲームを再生してみました。

f:id:simplestar_tech:20180917171841g:plain

これはTPSのフリーカメラですね!
歩かせてみます。

f:id:simplestar_tech:20180917172517g:plain

■まとめ
Unity 2018.3.0 beta で動かしました。
Cinemachine のプレビュー版をさわったのですが、素晴らしいカメラ操作を提供してくれるツールセットですね。
これを活用して三人称視点のゲーム作ってみたいと思いました。

ほかにもあらかじめ設定したレールの上を動くカメラなども学んだので、次に紹介したいと思います。

■追記
Free Look タイプのインスペクタービューの具体的な内容を記録します。
f:id:simplestar_tech:20181011221927j:plain

次の記事の予定

Unity に頂点カラーのマテリアルは用意されていません。
そこで、シェーダを多少はいじれるようにしておきたいと考えました。
そのほかUnityのシェーダーを編集できる UI の確認
タイムラインもそろそろ触れるようになっておきたいし
Cinemachine ってのも勉強したいです。

つまりは Unity 2017 で勉強してこなかったことを勉強していこうと思いました。

勉強の資料
nn-hokuson.hatenablog.com
こちらは読了、実践も済んでます
Unityのシェーダーを編集できる UI の Shader Graph には少しだけ慣れました。

こっちはシェーダーの基本を学ぶ資料です。ボリューム満点なので、ゆっくり読み進めます
7日間でマスターするUnityシェーダ入門 - おもちゃラボ

後で別記事でまとめますが、簡単にメモを残します。

なるほど Surface シェーダーという Unity 独特のシェーダーを使う場合に出てくる予約語はここから確認すればいいのか
docs.unity3d.com

Unity のシェーダーの設計思想が見えてきた
ライティングのシェーダーを書くのが難しく、また基本的に最先端の技術を取り入れて同じコードになるため
そこはラッピングして
ピクセルシェーダーを技術者に書かせるかわりに、ライティングの一歩手前のサーフェースシェーダーを用意し
頂点とサーフェースシェーダーだけを技術者は書けば良いように設計している。

Unity やさしい

シェーダーのプロパティ追加書式

docs.unity3d.com

ところで _BaseColor を宣言なく利用できていたけど、どこかで予約されていたりするのかな?
公式ドキュメントに一覧が書いてあるなら知りたいです。

そろそろ別記事書きます。

Unity:2018.2以降のPrefabとAssetBundleの変化を追う

ココが変わる!Unityの新しいエディターワークフロー
CEDEC2018

いつもお世話になっている「テラシュールブログ」の中の人の発表です。
これだけ Slide Share にアップされていない様子なので、記憶を頼りに
と思ったら Youtube に動画を発見した
www.youtube.com

Prefab と AssetBundle について簡単におさらい

  • Prefab

Prefab はシーン内のゲームオブジェクトをアセット化したものです。
元となるのはゲームオブジェクトなので、Prefab をもとにシーン内でゲームオブジェクトを復元するようにしてインスタンス化します。
このとき Prefab 化(アセット化)したときと同じコンポーネント構成でシーン内に登場します。

  • AssetBundle

巨大な動画、音声、画像ファイルをアセットとして外部サーバーに配置してゲームで必要になったときにダウンロードして使う仕組みで
とても一般的なゲームの実装方法ですが、共通のシステムは存在せず、各社で技術者を雇うか育てるかして独自実装してきました。
このアセットを外部サーバーに置く、必要になったらダウンロードすることを「AssetBundle」と呼びます。

今回の記事で注目するべきこと

■既存の Prefabの問題
Prefab の2 階層目以降の編集ができない
Prefab の下の階層に Prefab を配置できない
とても似ている Prefab 派生オブジェクトを一括編集できない
Prefab の編集の適用は Prefab 単位でしかできない
参照アセットは強制で一括読み込みするので、完了までゲームがフリーズする
AssetBundle に Prefab を置くと動作を確認するのに、長時間かかる(ちょっとした修正にAsset Bundle のビルドが必要)

一言でまとめると、Prefab を扱うために面倒を見るためのエンジニアやコードが必要なことが問題
もっと楽に Prefab を扱いたい

そこで新しい Prefab Workflow が登場しました。←ここが注目するべきこと

■ New Prefab Workflow
1.アイコンからゲームオブジェクトと Prefab を識別できる
2.Prefab の編集した値が太くなるだけではなく、項目の先頭が青く色付く
3.Prefab の何を編集したかを確認する UI が新しく追加される
これには項目を選択すると編集前と編集後のインスペクタを比較する機能がある
Prefab の変更の適用をフィールド単位で行えるようになった
4.Prefab を編集するシーンを開くボタンが追加された(Prefab エディター)
5.Prefab の子階層に Prefab を配置できるようになった(Nested Prefab)
値のオーバーライド(適用)では、どの階層の Prefab がオーバーライド(適用)を行うかを指定できるようになる
発表動画では詳しくデモを通して解説があり、新しく追加された3.Prefab の何を編集したかを確認する UI にて指定できる
6.Prefab の Variant (コンテキストメニューの Create > Prefab Variant を選択で作成)
Prefab なのに個別に値をオーバーライドできるようになり、つまりは、ちょっとだけ内容が違う Prefab を量産できるようになり
さらにオリジナルの変更が Variant にも反映されるので、とても似ている Prefab 派生オブジェクトを一括編集する操作が Variant を使って可能になる
7.モデルの更新でもコンポーネントを保持
今までモデルを差し替えたときにボーン構造が異なると、モデルの Prefab は初期化されてしまっていたが
Nested Prefab の仕組みが入ったので、見た目だけの更新だけで、モデルに与えていた様々なコンポーネントなどが保持されるようになった

■Addressable Asset System
これまでのアセット参照はドラッグ&ドロップだった
ただし直接参照は強制で一括読み込みするので、完了までゲームがフリーズする

既存のロード手段は三つ
・直接参照
・Resources (非推奨)
・AssetBundle (難易度高い)

新しい Unity は以下の2つに変更された
・直接参照
・Addressable Asset System

導入は Package Manager を通して Addressable System を選択して導入できる
コードは以下のように修正する

[SerializeField] GameObject player;
↓
[AssetReferanceTypeRestriction(typeof(GameObject))]
public AssetReference player;

アセットの登録は今まで通りドラッグ&ドロップ

インスタンス生成のコードが変わる

GameObject playerInstance;
playerInstance = Object.Instantiate(player);
↓
GameObject playerInstance;
player.Instantiate<GameObject>().Completed += (op) => { playerInstance = op.Result; }

インスタンス生成が非同期で行えるようになるコード例が登場
(読み込み完了後のインスタンスへの操作も、ラムダ式の中で書ける)

オブジェクトの破棄も変わったので注意

Destroy(playerInstance);
Resources.UnloadAsset(asset);
↓
Addressables.ReleaseInstance(playerInstance);
Addressables.ReleaseAsset(asset);

■Addressables ウィンドウ
AssetBundle に登録したアセットを一覧でアドレスを確認でき、検索用のタグも付けられる

var preloadOp = Addressables.PreloadDependencies("label_name", null);
yield return preloadOp;
public AssetLabelReference hazardsLabel;

var op = Addressables.LoadAssets<IResourceLocation>(hazardsLabel.labelString, null);
yield return op;
hazardLocations = new List<IResourceLocation>(op.Result);

でラベル一致のアセットを事前ロードできる書式が追加されました、便利です。

アドレスから直接オブジェクトを生成する手段もある
Addressables.Instantiate("Address");

さらにこのウィンドウではグループごとに AssetBundle を配置するパスを指定可能
ローカルパスでもサーバーのリモートパスでも可
この設定はプロファイルとして Default, Dev, Release などの名前を付けて、選択一つで切り替え可能になっています。(開発フローを意識したものです)

格納方式も選べる
1アドレスにつき1AssetBundle → pack Separately
グループを1AssetBundle → pack Together

■AssetBundle を用いた高速デバッグ
Play Mode を以下から選択できる
・Fast (AssetBundle 構築しない、AssetDatabase から直接アセット取得 なのでゲーム動作確認時に選ぶとよい)
・Virtual (AssetBundle 構築しない、しかしアセットの依存関係のみを本番と同じにできる、本番想定の関係性構築で利用する)
・Packed (AssetBundle を構築、AssetBundle からアセットを取得 なので本番想定のパフォーマンスをチェックできる)

■PM Profiler
Resource Profiler とも言う
Addressables まわりの操作で生まれたアセットのロード、開放などをプロファイリングすることができる

結論:Addressable Asset System に移行すれば、色々考えなくて良くなる

Unity:パフォーマンス表示uGUIの作り方(2018版)

最近 ECS + Job Systems + Burst Compile と SRP の習熟度を上げましたが、今度は開発中にゲーム内のUIでパフォーマンスをチェックする機構について興味がわいたので
最新の Unity のパフォーマンス計測テクニックを参考に作ってみたいと思います。

ちょっと豆知識ですが、職場の先輩がパフォーマンスを損なうコードを書いた人を犯罪者と呼ぶのですよ。
たとえばプロファイルしたときのCPU処理時間でスパイクが立つとき、その原因となった関数を組み込んだ人を犯罪者と呼びます。

今回手掛けるツールは「あー、なんか犯罪したかも」を、素早く発見することを提供するものです。
他人が書いたコードに手を入れずに
「あなたの書いたこの関数が遅いと思うんです」→「あなたの書いたこの関数が処理時間の8割で30ms要しています、高速化の検討・対処が現在のチームの最優先課題になっています!」
と第一声の内容が変わって、チームメンバーが各々の作業に深く納得しながらお仕事できるようになるわけです、とても素敵ですね!

まずは cedec のこの講演を見てみましょう。

www.slideshare.net

まずはおさらい
Unity でパフォーマンス計りたいと思ったら
Window > Analysis の下にある
Profiler (全体の負荷計測)と Frame Debugger (レンダリングの順番と内容)を表示します。
f:id:simplestar_tech:20180908221157j:plain

Profiler の Overview にて、大雑把に関数ごとの処理時間を知ることができ
Timeline 表示で他のスレッドの処理の様子も見ることができます。(2017.3以降、かつスクリプトに開始と終了のコードを仕込む必要があります)
実装詳細はこちら
Unity - Scripting API: Profiling.Profiler.BeginThreadProfiling

CustomSampler sampler = CustomSampler.Create("Exec");
Profiler.BeginThreadProfiling("My threads", "My thread 1");
sampler.Begint();
// ... スレッドの処理
sampler.End();
Profiler.EndThreadProfiling();

をスレッドの関数内で記述します。

また Editor で開発中は Deep Profile を有効化してさらに細かい関数ごとのパフォーマンスをチェックすることが可能になります。
Android でも adb shell am start -n [パッケージ名]/[アクティビティ名] -e "unity -deepprofiling" コマンドで Deep Profile を有効化できるようになりました。(2017.3より)
Windows マシンに Android を接続した状態で AndroidPlayer を対象に選べば Development Buildを有効にしたアプリであれば Android 実機上でのパフォーマンスも計測できます。
さらに自身で定義した処理区間を Profiler に名前入りで表示することも可能です。
具体的には以下のコードで

    CustomSampler sampler;
    void Start()
    {
        sampler = CustomSampler.Create("MyCustomSampler");
    }

    void Update()
    {
        sampler.Begin();
        // do something that takes a lot of time
        sampler.End();
    }

Profiling.CustomSampler.Begin - Unity スクリプトリファレンス

・Frame Debugger は Unity の描画処理を 1 ステップずつ確認できるレンダリングにフォーカスした確認ツールです。
・Console ウィンドウ
Debug.Log 関数の内容を表示する。(基本ですね)
Console ウィンドウも対象を選択できる機能があり、Player Logging を有効化して Android Player を選べば Development Build を有効にしたアプリのログを表示することができます。

・Script Debugging
こちらも基本ですが、Visual Studio の UI から Unity にアタッチする操作が可能です。
アタッチできれば、Visual Studio の方で配置したブレークポイントでブレークして変数の中身のウォッチなどのデバッグを行うことが可能です。(Visual Studio での開発からUnity開発に流れてきた人にとってはこれが一番うれしいデバッグ機能ですね)
実機の場合でもこのScript Debugging を行うことが可能ですが、ディフォルト設定だとできないという落とし穴を用意していますので、知らない人はずっと苦しむことになります。
具体的にはPlayer Settings にて Mono か IL2CPP を選ぶ項目にて Mono を選んでいる状態かつ、Unity からのビルド時に Development Build かつ、Script Debugging のチェックボックスにチェックを入れている場合のみ、C# Script Debugging を行うことができます。
Visual Studio のUIにて「Unity にアタッチ」の他に「Unity デバッガーのアタッチ」を選べるはずなので、そこで AndroidPlayer(USB)を選択します。
この三つの条件+Visual Studio のUI操作をすべて満たしていないと絶対に Script Debugging できませんので、実機でのパフォーマンスうんたら言う人は必ずエンジニア全員にこのデバッグ方法を周知してから作業に取り掛かってください(この実機でのスクリプトデバッグ方法の設定をチームメンバーに周知しないプロジェクトはこの世に存在しません。実機でデバッグせずに開発を続けることはあってはならない!)
なお Unity 2018.2 からは IL2CPP でも Script Debugging が可能になりました。ただし、こちら .Net 4.x Equvalement にのみ対応しています。(※初回アタッチだけ時間を要する模様)

あとはプラットフォームごとのツールを使って CPU の利用状況などを見ていきます。
PC/Android は RenderDoc
iOS は Isntruments, OpenGLES Frame DEbugger
Android は Systrace, Snapdragon Profiler, GAPID などなど

一瞬ちらつくなど、対処が難しすぎるバグに対応するときはこうした外部ツールを使い始める場面もあるでしょう。

そのほか Unity Prfiler にて示されているものは Recorder API から取得可能です。
具体的には

Recorder recorder = Recorder.Get("Camera.Render");
float ms = recorder.elapsedNanoseconds * 0.00001f ;// で ms オーダーの処理時間を取得

といったコードで取得できます。

メモリの使用状況やヒープサイズなんかも Profiler API から取れるそうです。

// Manager Memory 使用状況
Profiler.GetMenoHeapSizeLong
Profiler.GetMenoUsedSizeLong
// Unity が確保したメモリの使用状況
Profiler.GetRuntimeMemorySizeLong
Profiler.GetTotalReservedMemoryLong
Profiler.GetTotalUnusedReservedMemoryLong

読み込んだアセット一覧なんかも

Resources.FindObjectsOfTypeAll

を利用することでメモリ上にあるアセットを列挙(※Editor 上では余計なものも列挙するので実機上で行う必要あり)
Texture2D, Meth, AudioClip, AnimationClip 一覧を上記関数で調べられるとのこと

なお、こんなことしなくても、リソースごとにどれくらいメモリを使っているかを示す新しい Memory Profiler が 2018.3 から Package Manager 経由で Alpha Preview できるようになるとのこと
これからもっとメモリのプロファイリングは、取得したい情報が取れるようになっていくとのことです。

最後に示されたのは実機で Profiler のログを残すという手段

Profiler.logFile = Path.Compine(Applicaton.persistentDataPath, "profiler.log");
Profiler.enableBinaryLog = true;
Profile.enabled = true;
// ... 計測対象ループ
Profile.enabled = false;
Profiler.logFile = null;

こうして記録したファイルを実機から取得するには
Android だと adb pull filepath で PC 側にファイルを取得可能
iOS だと info.plist でファイルアクセス許可を追加すると、iTunes のアプリ項目からファイルのダウンロードが可能になります。
そうして手に入れたファイルは Unity Profiler の Load ボタンからファイル選択して読み込み可能です。

さて、ここで大問題!
Unity Profiler は最新の 300 frame しか表示することができないので、それより長い Profiler ログを渡しても全部確認することができません!
そうした問題に対応するようにプラグインを発表者の方は作成されています。
github.com
300フレーム問題で苦しみ出したら、このプラグインを使わせてもらいましょう。

本当に最後は 2018.2 より提供された ResourceManager のための Profler
RM Profiler の話で締めくくられました。

ここまでの話を、再度おさらいするのに、テラシュールブログの方の解説も読むと漏れなく学習できると思います。(いつも感謝です)
tsubakit1.hateblo.jp

■ここから本題
スライドのチェックはここまでにして
本題の実機上で一目でわかるプロファイル情報表示UIを作るための書式確認を始めたいと思います。

と思ったら、発表スライドにて、そのUIツールの実装へのリンクが示されていました。
github.com

導入の仕方:
リポジトリをダウンロードしたら MainLoopProfiling.unitypackage を起動して Prefab と .cs ファイル x4 をインポート
Prefab をシーンに配置すると UI が完成

ソース内容はここまで読めばわかると思いま…いや、わからなかったですね。
実装を詳しく見ていくと Unity 2018.1 から使える PlayerLoop を活用したものであることがわかります。
色々なループの型があるのですが、代表的なものとしてこんなものが定義されています。

namespace UnityEngine.Experimental.PlayerLoop
{
    public struct Update
    {
        public struct ScriptRunBehaviourUpdate

        public struct DirectorUpdate

        public struct ScriptRunDelayedDynamicFrameRate

        public struct ScriptRunDelayedTasks
    }
}

こうした型を指定して、例えば ScriptRunBehaviourUpdate の前後に関数を仕込ませ、開始と終了の間の時間を Time.realtimeSinceStartup の値で計測
これを集計して UI のアンカーポイントをずらしながら色付きのバーを動かしていました。
Android の MultuThreadRendering の仕組みまで考慮して Rendering 時間を計測するコードが書かれており、私が手を入れる部分が無いですね
FPS テキストは 1 秒に一回の頻度で更新するように書かれていました。
Unity 2018.1 以降で開発される方はこの計測器を配置してゲームを開発されるとよろしいのではないでしょうか?

こちら Android (Xperia XZs Android 8.0.0) で Basic Rendler Pipeline の描画シーンをキャプチャした画面です。
f:id:simplestar_tech:20180909180934p:plain

Main Thread はレンダリングに比重を置きすぎかもしれませんね。(ビルド設定では MultiThreadRendering 有効化しているけど、実機が対応できていないのかな?)
ただ 60 FPS 出ているのはさすが

もうすぐ AppleiPhone の新しい発表が待っているし、そろそろ iOS で開発してみたいもんです。
新しい iPhone 買っちゃおうかなぁ
Mac Book Air も買っちゃおうかなぁ

Unity:ScriptableRenderPipeline(SRP)の書式確認

Unity の Scriptable Render Pipeline(SRP) とは端的に言うと、Unity がフレームをどのようにレンダーするかをデベロッパーが C# で制御できるようにするものです。(大事なことなので何度でも)

前回の記事でSRPの導入方法はバッチリ
simplestar-tech.hatenablog.com
今回は、前回記事の最後に確認した最も単純な BasicRenderPipeline の実装について確認していきます。

具体的には次の BasicRenderPipeline.cs コードを詳説して終わります。
github.com

定義されているクラスは三つ
1.public class BasicRenderPipeline : RenderPipelineAsset
Unity のプロジェクトから選択できる SRP アセットを定義しています
2.public class BasicRenderPipelineInstance : RenderPipeline
上のアセットに紐づくパイプラインで、実装は次のクラスの Render 関数を呼ぶだけ
3.public static class BasicRendering
ScriptableRenderContext renderContext, Camera cameras を受け取って、実際にパイプラインを実装している静的クラス

終始 public bool UseIntermediateRenderTargetBlit; フラグで分岐するコードがあるが、これはシーンからユーザーがチェックボックスで設定するもので、基本 false の値となっている。
シーンで切り替えられるように配置したフラグをネストが深いところまで連絡しているコードが全体を汚しているだけなので
最初は UseIntermediateRenderTargetBlit の連絡コードと分岐先の実装は無視して読むと、認識負荷は大きく落とせます。

そうした上で BasicRendering クラスの実装の中身は次の通り
1.ScriptableRenderContext renderContext, Camera cameras を受け取る、静的な Render 関数の実装
2.その Render 関数で利用する ConfigureAndBindIntermediateRenderTarget 関数(UseIntermediateRenderTargetBlit == true の時のみ実行するので無視)
3.その Render 関数で利用する BlitFromIntermediateToCameraTarget 関数(UseIntermediateRenderTargetBlit == true の時のみ実行するので無視)
4.その Render 関数で利用する SetupLightShaderVariables 関数
5.その Render 関数で利用する GetShaderConstantsFromNormalizedSH 関数

最初に通しで読む関数は 1.4.5.だけです。
1.では camera のループを回し、camera から見えないオブジェクトを削ぐようにカリングする実装から始まり
コンテキストに camera の設定を入力して、CommandBufferPool からコマンドを取得し depth のみクリアしています。
続いて global lighting shader variables を最大 8 個まで更新して RenderQueueRange.opaque と不透明オブジェクトのみを扱うようにフィルタリングして、近い順に描画しています。
そのあとに余ったピクセルに Skybox を描き、最後に RenderQueueRange.transparent と半透明オブジェクトのみを扱うようにフィルタリングして、遠い順に描画しています。
camera 単位の描画処理を記述したら context.Submit(); して次の深度のカメラについて処理していきます。
stereoEnabled フラグは XR でデバイスを検知しない限り false なので、ほとんどのコードを無視してパイプラインの実装を読むことが出来ました。
以上です。

これはわかりやすい。
では少し弄ってみましょう。

不透明オブジェクトとSkyboxを描画してから半透明オブジェクトを描画していますが
半透明オブジェクトの描画を先頭に持ってくるとどうなるでしょうか?

f:id:simplestar_tech:20180909125955j:plain

半透明オブジェクトが…

f:id:simplestar_tech:20180909130230j:plain

不透明オブジェクトで上書きされてしまいました。
Unity にはオブジェクトごとのレンダリング結果を見る Frame Debugger がありますので、そちらを使って描画順序を確認してみましょう。
するとこんな感じ

f:id:simplestar_tech:20180909131718g:plain

なるほど、半透明を描画してから不透明で上書きしていることがはっきりわかりますね。
Sort フラグもちょっといじりますか
None を設定したところ、Plane の描画順番が早い段階に移動してしまいました。
これでは、後で不透明オブジェクトで上書きされてしまう無駄なピクセルの計算が走ってしまいます。

f:id:simplestar_tech:20180909132447j:plain

なるほど、こんな感じで FrameDebugger と連携しながら、シーン描画を最適化するようにパイプラインを弄っていけば、かなり描画パフォーマンスを意識したパイプラインを構築できるようになっていくのではないでしょうか?
また少しレベルアップ出来た気がします。

Unity:2018のScriptable Render Pipelineに慣れる

Scriptable Render Pipeline(SRP) とは端的に言うと、Unity がフレームをどのようにレンダーするかをデベロッパーが C# で制御できるようにするものです。

ついに Unity のブラックボックスに手を入れられる時代が始まろうとしています。
これを活用できるかどうかがゲーム会社として生きるか死ぬかの分かれ目になりそう。
たくさんの同一オブジェクトをシーンに配置するようなゲームでは特に、パフォーマンスを上げる手段として使えるため
SRPを使えるようになっておかないといけないでしょう。

参考資料は先週開催された cedec 資料のこちらです「Scriptable Render Pipeline を使ってみよう」

www.slideshare.net

まずは今までのブラックボックスレンダリングをホワイトボックスに切り替える具体的な操作から確認します。

参考資料のさらに参考資料として、凹みさんの記事を読みました。
tips.hecomi.com

今ある Forward, Deferred レンダリングでうまくない点
既存の問題点を洗い出して、解決策としてのレンダリングを制御できる API の用意は自然な流れ
それが Scriptable Render Pipeline (SRP) という理解が得られました。

今回試した環境
Unity 2018.2.6f1

では SRP に切り替える方法ですが
Package Manager を起動して SRP 関連をインストールします
Create > Rendering > Lightweight Pipeline Asset
を選択してプロジェクトに SRP オブジェクトを作成

メニューの Edit > Project Settings > Graphics を選択してインスペクタを表示し
f:id:simplestar_tech:20180904215541j:plain
この Scriptable Render Pipeline の項目に、作成した SRP を指定します。

以上

確かに描画が切り替わったようです。
マテリアルが不正な状態となって、Unity特有のどぎついピンク色でべた塗りされてしまいました。
Lightweight Pipeline Asset オブジェクトを選択してインスペクタを確認すると次の通り
うん、解説がないとわからないですね。

f:id:simplestar_tech:20180905065748j:plain

ここでスライド資料にて紹介されていた次の ReadMe をすべて読みました。(SRPのソースコードもここから確認することになります)
github.com

読んでいて、結局こちらも必見でした。(読みました)
blogs.unity3d.com

これらを読んで新しく手に入った知見は
1.シーン内のマテリアルは Lightweight Pipeline 用に作り直す必要があること
2.その新しいマテリアルに作り直す具体的な方法
3.プロジェクト設定はスクリプトから GraphicsSettings.renderPipelineAsset プロパティでアサインできること
でした。(それだけ)

具体的な Lightweight Pipeline の導入手順についてはこちらの方が分かりやすかったです。
tsubakit1.hateblo.jp

では最後に Sample Scenes のリポジトリとして紹介されている次のリンクを確かめます。
github.com
HDRP, LWRP 両方が必要なので、PackageManager から設定しました。
TestbedPipelines フォルダの BasicRenderPipeline シーンから確認してみましょう。

実行するとこんな絵になります。
f:id:simplestar_tech:20180908120059j:plain

シーンのヒエラルキー内のスクリプトコンポーネントはたった一つで以下のコードでした。

using System;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class SceneRenderPipeline : MonoBehaviour
{
    public RenderPipelineAsset renderPipelineAsset;

    void OnEnable()
    {
        GraphicsSettings.renderPipelineAsset = renderPipelineAsset;
    }

    void OnValidate()
    {
        GraphicsSettings.renderPipelineAsset = renderPipelineAsset;
    }
}

シーンで設定した RenderPipelineAsset をエディタ変更時またはシーン開始時に GraphicsSettings に設定するだけ
実際に設定したのは BasicRenderPipeline です。
こちらはまさに自作の RenderPipeline で次の実装から作られていました。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.XR;

// Very basic scriptable rendering loop example:
// - Use with BasicRenderPipelineShader.shader (the loop expects "BasicPass" pass type to exist)
// - Supports up to 8 enabled lights in the scene (directional, point or spot)
// - Does the same physically based BRDF as the Standard shader
// - No shadows
// - This loop also does not setup lightmaps, light probes, reflection probes or light cookies

[ExecuteInEditMode]
public class BasicRenderPipeline : RenderPipelineAsset
{
    public bool UseIntermediateRenderTargetBlit;

#if UNITY_EDITOR
    [UnityEditor.MenuItem("Assets/Create/Rendering/Basic Render Pipeline", priority = CoreUtils.assetCreateMenuPriority1)]
    static void CreateBasicRenderPipeline()
    {
        var instance = ScriptableObject.CreateInstance<BasicRenderPipeline>();
        UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/BasicRenderPipelineTutorial/BasicRenderPipeline.asset");
    }

#endif

    protected override IRenderPipeline InternalCreatePipeline()
    {
        return new BasicRenderPipelineInstance(UseIntermediateRenderTargetBlit);
    }
}

public class BasicRenderPipelineInstance : RenderPipeline
{
    bool useIntermediateBlit;

    public BasicRenderPipelineInstance()
    {
        useIntermediateBlit = false;
    }

    public BasicRenderPipelineInstance(bool useIntermediate)
    {
        useIntermediateBlit = useIntermediate;
    }

    public override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
    {
        base.Render(renderContext, cameras);
        BasicRendering.Render(renderContext, cameras, useIntermediateBlit);
    }
}

public static class BasicRendering
{
 ...
}

なるほど、エディターで扱える Asset と実行時に利用する Pipline を合わせて実装する必要があるのですね。
気になる BasicRendering の実装はぜひ最新の次のページをご覧になってください。
github.com

カメラを指定して SkyBox を描くコードを見つけられると思います

// Draw skybox
context.DrawSkybox(camera);

このコードをコメントアウトしてみたところ、すぐに反映されて背景の Skybox の描画が行われなくなりました。

f:id:simplestar_tech:20180908125329j:plain

おおー、今私は Unity の Rendering Pipeline を直接いじれていますよ!
ここを足掛かりに、すこしずつ SRP に慣れていこうと思います。

Unity 最高!