はじめに
こんにちは、サイバーエージェントゲームエンターテイメント事業部、SGEコア技術本部(コアテク)のグラフィックスチームに所属している清原です。
今回は、コアテクのグラフィックスチームで取り組んだ「UnityのTest Runner上でNVIDIA製の画像比較ツールFLIPを用いたビジュアルリグレッションテストを実施できるようにするための検証・組み込み」についてご紹介します。
ビジュアルリグレッションテストとは
ビジュアルリグレッションテストとは、グラフィックスの品質を自動的に検証するためのテスト手法です。主に以下のような目的で使用されます。
- シェーダーの変更による見た目の変化の検出
- レンダリングパイプラインの変更による品質の劣化の検出
- 異なるプラットフォーム間での見た目の違いの検出
コアテクのグラフィックスチームでは外部に公開しているNova Shader、Air Stickerや社内向けOSSのSIRIUSなどの開発を行っています。
これらの開発を行う際に、Unityのアップデート、最適化などで見た目に変化が生じてしまったり、新機能を実装したことで、その機能とは関係のない箇所で副作用が起きて、描画不具合が起きてしまったりするということがあります。
下記の図はUnity 6でRender GraphとCompatibleモードで描画結果が異なっている様子です。
Render Graph | Compatibleモード |
---|---|
![]() |
![]() |
この絵は一見すると違いがないように思えますが、NVIDIAのFLIPを利用して画像の差分のヒートマップを抽出すると次のような差分が生まれていることが分かります。

FLIPによる差分のヒートマップ
特に、Nova ShaderやSIRIUSは多機能なレンダリングシステムとなっており、副作用が発生する可能性が高くなります。 そのような問題を軽減するために、これらのライブラリは日々の開発でビジュアルリグレッションテストを実施できる環境を構築しています。
Unity Graphics Test Framework
さて、ここまで画像比較のツールとしてFLIPを紹介してきましたが、FLIPの検証・組み込みの話の前に、Unity公式のUnity Graphics Test Frameworkというフレームワークについても紹介させてください。
このフレームワークはUnityのパッケージマネージャーからインストールすることができます。
NVIDIAのFLIPをTest Runnerで動かすには多少のセットアップが必要になりますが、このフレームワークはお手軽にインストールできて、簡単にTest Runnerで実行することができます。
コアテクでもFLIPの検証・組み込みを行う前はこちらを利用していて、「新規機能を開発したのでコミットする前にテストを走らせる」、「PRを作る前にテストを走らせる」などのように、日々の業務でビジュアルリグレッションテストを実行していました。

Test Runnerでテストを実行
Unity Graphics Test Frameworkの画像の比較テストは大きく分けると3つのテストがあります。
今回はこのうち平均値テストに絞ってお話をしていきます。
Unity Graphics Test Frameworkの平均値テスト
Unityの平均値テストは画像をJzAzBz色空間
に変換して差分の平均を計算し、差分が閾値以上の場合にエラーを検出します。
JzAzBz色空間
とは2020年にグレイム・W・ロス(Graham W. E. Rust)とマイケル・H.ブレットル(Michael H. Brill)らによって提案された、最新の知覚ベースの色空間です。この色空間は「高ダイナミックレンジ(HDR:High Dynamic Range)」や「広色域」表示に対応するよう設計されており、特に人間の視覚特性に基づいて色の違いをより正確に表現できることを特徴としています。
そのため、この色空間は画像テストにおいて良好な結果を返す可能性が高いものになります。
しかしながら、Unityの画像比較はシンプルな平均値となっており、差分が平坦化されるため、下記のような大きな変化があるようなシーン(反射のあり/なし)においてもエラーを検出できないケースが存在しました。

エラーが検出できない例
もちろん、このエラーは平均値テスト
の閾値の調整や、他の不正なピクセル数の個数テスト
などと組み合わせれば検出することは可能です。しかし、調整する閾値のパラメータ数が多くなってしまうため、良好な結果を得られる数値調整の難易度が上がってしまいます。
そこで、より良好な画像比較の結果を得られるツールを探してNVIDIAのFLIPを検討することとなりました。
NVIDIA FLIP
FLIPは人間が2つの画像を交互に見る際に知覚する違いを近似した差分マップを生成するツールで、その差分マップから知覚的な重み付けを行った差分の平均値などの統計値も取得することができます。
Unityの平均値テスト
と詳細な手法は異なりますが、人間の視覚特性に着目して差分を抽出している点は同様です。ただし、知覚的に変化が大きいと判断される差分に重みをつけることによって、他の画像指標よりも精度の高いエラー検出が可能であると紹介されています。
FLIPをTest Runnerで使う方法
さて、FLIPのエラー検出精度が高そうなことが分かったので検証を行いたいのですが、Test Runnerで動作するテストが非常にお手軽なため、そのフレームワーク上でFLIPを実行したいというモチベーションがありました。 FLIPを利用する方法はC++、Pythonなどがあるのですが、今回は一番簡単に利用できそうだったPythonを使ってみました。 Pythonで利用するには下記を行う必要があります。
なお、Pythonでセットアップ/利用のより詳細な説明は公式のマニュアルを参照してください。
あとは、最後のflipのAPIを下記のようにC#側から呼び出すだけです。
// コマンド var command = "flip"; // コマンドライン引数 var args = "-r \"reference.png\" -t \"test.png\""; var psi = new ProcessStartInfo(); psi.FileName = command; psi.Arguments = args; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; psi.CreateNoWindow = true; using var process = Process.Start(psi); var output = process.StandardOutput.ReadToEnd(); Debug.Log(output);
flipを実行すると次のような出力結果を返してきます。今回はこの結果をパースしてテストに利用しました。
Mean: 0.096946 Weighted median: 0.235086 1st weighted quartile: 0.074048 3rd weighted quartile: 0.488955 Min: 0.000000 Max: 0.817382
これらの値は差分マップの統計情報であり、下記のような意味を持ちます。
パラメーター | 説明 |
---|---|
Mean | 差分マップの重み付き平均値 |
Weighted median | 重み付き中央値 |
1st weighted quartile | 第1重み付き四分位数。差分マップの値の下位25%がこの値以下であることを示します。 |
3rd weighted quartile | 第3重み付き四分位数。差分マップの値の下位75%がこの値以下であることを示します。 |
Min | 差分マップにおける最も小さい差分値 |
Max | 差分マップにおける最も大きい差分値 |
このうち今回使用するのは、差分マップの重み付き平均値のMean
です。
NVIDIA FLIP vs Unityの平均値テスト
Unity上でFLIPを利用できるようになったので、Unityの平均値テストとエラー検出の精度を比較してみました。
次の表はUnityで同一の環境(WindowsでビルドターゲットはAndroid)での描画結果でテストを行った結果です。 同一環境ではありつつ、乱数的なアルゴリズムを使っているシーンがあるため、人間の目で知覚できる違いが6件存在しています。
平均値テストの閾値 | Unityの平均値テストのエラー検出数 | FLIPでのエラー検出数 |
---|---|---|
0.05 | 0件 | 1件 |
0.025 | 0件 | 5件 |
0.01 | 0件 | 6件(過剰反応を含まない、一番良い結果) |
0.005 | 0件 | 13件(過剰反応) |
0.0025 | 1件 | 14件 |
0.001 | 1件 | 14件 |
0.0005 | 5件(過剰反応を含まない、一番良い結果) | 17件 |
0.00025 | 5件 | 18件 |
0.0001 | 6件(過剰反応1件含む) | 18件 |
結果的にFLIPは閾値を0.01にした時に期待していた6件の検出ができましたが、Unityの平均値テストは期待していた6件を検出することはできませんでした。 (ここでいう過剰反応は私の目では違いが認識できなかった検出を指しています)
さらにこの結果で最も結果の良かった閾値を用いて異なる環境(WindowsでビルドターゲットはPC)でどれだけエラーを検出できるかも確認してみました。 AndroidからPCにビルドターゲットを変更しているため、テクスチャの圧縮形式やUnityの内部のシェーダーの処理も微妙に変化が起きています。
画像テスト(閾値) | エラー検出数 |
---|---|
Unityの平均値テスト(0.0005) | 11件検出 |
FLIP(0.01) | 23件検出(過剰反応なし) |
このように、FLIPは23件のエラーを検出できたのに対し、Unityの平均値テストでは11件にとどまりました。
まとめ
FLIPとUnityの平均値テストだと、テストの精度はFLIPがかなり高いという結果になりました。ただし、Unityも平均値テスト
+不正なピクセル数テスト
などを組み合わせて閾値などの各種パラメータを調整するとFLIPと同等の結果を得ることは可能かもしれません。しかし、調整するパラメータ数が増えていくため調整の難しさも感じています。
FLIPは平均値だけのテストでUnityの複数のテストを組み合わせたテストと同等以上の精度が出せたので、テストの調整がしやすそうに感じました。
弊社が公開しているOSSのNova ShaderでFLIPを利用していますので、ご参考になれば幸いです。