Unreal Engine + OpenColorIOを使ってACEScgのリニアRGB値を取得する
目的
Unreal Engine (UE)の作業色空間であるACEScgの値に基づいたリニアRGBを取得できるようにしたい.
とりあえずの結論
イントロ: Unreal Engineでの色に関する生のデータ欲しい
Unreal Engine (UE)は色を計算する際,リニアRGBを利用するシーンリニアワークフローという手段が取られています.というのも実世界の光はリニアの性質があり,リアルの挙動を再現するためです.ただ,UEのエディット画面で見えているComputer Graphics (CG)の画像は ノンリニアRGBで構成されています.
通常ゲームを作る際は計算はリニア,表示はノンリニアで良いのでこの体系で良いのですが,今回は情報工学の研究関係でリニアRGBの画像を表示したり,リニアRGBの値が欲しいので,デフォルトの状態からUEのリニアRGBを取り出すMethodを調べてみました.また、UEさわるの初めてなのでもしかしたら勘違いしているところもあるかもしれません.
プロジェクトの準備
Working Color SpaceをACEScgに設定する
今回,UEを下記の環境で動かしています.とりあえずプロジェクトはGameのカテゴリにあった、"Blank"のプロジェクトをベースにしています.
実際に操作する前に今回のプロジェクトのWorking Color Spaceを決めます.Working Color Spaceとは上で述べたリニア色空間を何にするのかを設定するところです.今回はACES AP1 / ACEScgに設定します.
ACEScgは映画芸術科学アカデミーが提案している色空間であり,表示用ではなく最初からWorking Color Space用として開発されたものであり,デフォルトでリニアRGB仕様になっています.ACEScgをWorking Color Spaceとして開発するフローはACESリニアワークフローと呼ばれ,UEだけでなく映画や画像を作る現場でも使われ始めています.ACEScgに加え,ACES AP0などの色空間もあるのですが,ACESの話を進めるとそれだけで膨大なコンテンツになるのでここでは省略.ですが思想や歴史は興味深いです.
また,これはこちらの研究の都合でですがGlobal IlluminationとReflectionsをデフォルトのLumenからScreen Spaceに変更して実装しています.LumenはUE5がリリースされた際の目玉ライティングなのですが,研究で使用するHDRI (High Dynamic Range Image)の挙動がLumenではよくないということでScreen Spaceに変更しています.
HDRIの環境光とオブジェクトの用意
今回のテスト環境として,適当なHDRIの照明とUEの中に含まれているSM_ColorCalibratorを入れてみます.HDRIについてはプラグインHDRI Backdropがライトと背景画像を同時に設定できて便利なので利用しています.今回の背景もUEが元から入っているデフォルトのやつです.
また,露出を固定するためにPostProcessVolumeオブジェクトを追加し,オブジェクトの設定以下のような設定にします.
リニアRGBの画像はちょっと暗い
UEでリニアRGBを表示する前に,リニアRGBの画像がどのように見えるのか紹介します.
左が非線形のガンマ補正をかけたRGBで記録したもので,右がリニアRGBで記録したものです.画像自体は私が撮影したものですが,見た目に近いものは左の画像で,リニア画像はちょっと暗い印象です.なぜこのような現象が起きるのでしょうか?
現象を簡単に説明するために上の図を用意しました.上の段から見てみましょう.一番左はディスプレイの特性を表しています.RGBに対して線形に光を制御できたら良いのですが,実はディスプレイは入力されたRGBに対して非線形な光を出しています.ガンマ特性とも言います.そのため,画像フォーマットの方で生のRaw RGB値に対して,ガンマ補正をかけ,わざと非線形なRGB値を作ります.このRGB値をディスプレイ入力することで,Raw RGB値に対して線形な光を制御できるようになります.
一方下段の方では,リニアなRGBはディスプレイのガンマ特性を直接表現するような形で画像を表示してしまいます.そのため,Raw RGB値に対して,中間層が思っているよりも暗い画像が表示されてしまうということになります.このあたりの現象もディスプレイのEIZOのページがわかりやすく解説していてくれています.
UEはデフォルトの状態でリニアRGBの画像を出力するのか?
実際にUEのデフォルトでWorking Color Spaceな画像を出力できるのか.調べてみると,ViewportのモードにScene Colorというモードが見つかりました.ドキュメントを確認してみると,
ポストプロセスというのは,計算されたリニアRGBに対して,ガンマ補正,トーンマッピング,色補正などディスプレイに表示するまでの様々な処理のことを指します.ということでScene Colorを表示してみましょう.
Viewportのモードは上の図のように切り替えることができます.ちなみに今表示している画像はUEがあらかじめ設定したポストプロセスがかかった状態です.
これでScene Colorになりましたが,これをみた時,
あんまり暗くなってないぁ…
そう思いました.そうです.リニアRGBの画像を出しているはずのScene ColorがリニアRGBの挙動っぽくないのです.これでは本当にRGBの値として合っているのか不安です.若干長くなりましたが,この疑問が今回の調査のきっかけになっています.Scene Colorがあまり信用できなくなった今,どのようにしてリニアRGBの画像を取得できるようになるのでしょうか.
解決策: OpenColorIOプラグインの使用
カラーサイエンスに精通している人なら,「早くOpenColorIO使ってよ」と思っているでしょう.お待たせしました.OpenColorIOでUEのViewportの画像の変化を調べてみます.
OpenColorIOとは簡単に言えば統一的な色空間変換プラグインです.制作現場では様々な色空間のマテリアルを利用して,様々な色空間のディスプレイを利用してコンテンツを作っていた背景があり,そのため安定した色管理が困難でした.そこでOpenColorIOは,上の画像のように統一的な規格を作ることで安定して色管理を行うために作られたものです.UE5はこのOpenColorIOに対応しているのでこれを使ってリニア色空間の表示をしてみます.
UEのプラグインページに飛んで,OpenColorIOを検索してチェックボタンを押すとOpenColorIOのプラグインが有効になります.有効にしたら次にOpenColorIOを使う準備をします.
1. OCIOファイルのダウンロード
ACESからOpenColorIOのConfigファイルが提供されているのでそれをダウンロードします.このConfigファイルには多様な色空間とその変換内容が記述されています.大抵このACESからのConfigファイルを使えば事足りると思います.また,ACESのConfigファイルには以下が用意されています.
cg-config
studio-config
reference-config
これは,制作するシチュエーションに応じて色空間のリストが若干異なっています.これは使用する状況に合わせたらいいと思います.今回はUEで使うので,cg-configのconfigファイルをダウンロードします.
2. OpenColorIO Configurationの作成
次にUE側でOpenColorIO Configurationを作成します.ここまでで忘れていましたがOpenColorIOの設定は上のUEのドキュメントに詳しく記載されてます.
Content FolderでOpenColorIO Configurationを作成したら,上のように設定を行います.設定項目について,詳しくは下の通りです.
さてここでようやく準備できました.OpenColorIOをビューポートをに反映させてみましょう.
OpenColorIOをViewportに反映
OpenColorIOにプラグインを入れると,Viewportのモードが新しく追加されています.ここに先ほど作ったOpenColorIO Configurationを追加し,変換元 (上の方)にACEScg,変換先 (下の方)をsRGB - Display - Rawを追加します.Enable Displayにチェックを押します.
よし!見事,OpenColorIOを適応することで,ACEcgのリニアRGBっぽい色を表示することができました.Scene Colorでは明るさが残ってたように見えますが,OpenColorIOを適応させることでリニアRGB特有の暗い画像を表示することができました.こうしてみると,やはりScene Colorはポストプロセス前のRGBとは言え,ACEScgのRawの情報ではないような気がします.
ACEScgのリニアRGB値の取得
ACEScgの画像を表示できたところで,実際の画像のRGB値を取得してみましょう.UEにはポインタ上のピクセルにおけるRGB値を調べるPixel Inspectorがあります.
このツールは,Tools->Debug->Pixel Inspectorから取得することができます.これを使って,カラーチェッカーのオブジェクトの色を抽出してみましょう.
上は,OpenColorIOを使用し,ACEScgのraw情報を表示させたカラーチェッカーにPixel Inspectorを使用してみた図です.Pixel Inspectorにおいて,Scene Colorはポストプロセス前の色,つまりOpenColorIOを適応する前のScene Colorno画像に基づいた色になってます.
それに対しFinal Colorとはポストプロセスをかけ,ディスプレイに表示する用の画像の色になっています.上の画像で色の違いを調べるために,Scene Color, Final Color, そしてターゲットとなった地点の色をスポイトで抜き出してみました.
すると,Final Colorとターゲット地点の色はほとんど同じようになり,Scene Colorの色は他よりも明るい色を示していました.このことから,OpenColorIOを使った処理はポストプロセス値の処理だと言うことがわかります.なので,ACEScgのリニア色空間を取得するときはFinal Colorの情報を参考にすると良さそうです.
考察: なぜScene Colorは明るく見えるのか?
とりあえずやりたいところまでやりましたが,ちょっとだけScene Colorが明るく見えることについて考えてみます.関連したものとして,以前のUE4において,マテリアルの色が思っている色よりも明るく見えてしまうというトラブルが話題になっていたらしいです.この中であった話題の一つにディスプレイガンマが影響しているのではないかと言うことがありました.
ディスプレイガンマとはUEで設定されているパラメータで,UEで表示されている画面全体に対してガンマ補正をかけるものらしいです.そこで,Scene Colorの画面にてディスプレイガンマの値を変更してみましょう.
ディスプレイガンマを変更するには,Debug Toolsを使うことになります.このツールは,Tools->Debug->Debug Toolsで見つけることができます.そこにGamma値を変えるとこにありデフォルトは2.2で設定されています.この値を変更してScene Colorの様子を見てます.
上はScene Colorにディスプレイガンマを変更した時の結果になります.見てみるとディスプレイガンマを2.2から1.0に変更したことで予想通り表示画像は暗くなりました.
また,上の画像の右にディスプレイガンマ2.2のそのままでOpenColorIOでACEScgのRaw情報を出した時の画像を載せています.発見としては,ディスプレイガンマを1.0にすることでScene Colorの表示画像が,ACEScg Rawに近くなったことです.やはりScene Colorが明るく見えていたのはディスプレイガンマの影響らしいです.
ただ,気になることもあります.ディスプレイガンマを変更してPixel Inspectorを使用してみると,Scene Colorはディスプレイガンマに対して値はほとんど変わっていないのですが,Final Colorでは値が異なっています.これは,Final Colorの値がディスプレイガンマの値を数値に反映するようにできているためです.
そして,ディスプレイガンマ1.0とは異なり,2.2の時ではFinal ColorとScene Colorとの値がほとんど一致しています.こう見てみると,Scene Color自体はポストプロセス前のRGB値だと思うのですが,あらかじめディスプレイガンマがScene Colorに考慮されているのでしょうか?UEのソースコードを軽く眺めてみましたが原因がいまいちわからなかったのでここ今後の課題として残しておきます.
結論
ここでは,UEのACEScgのリニア色空間を取得するまでの方法と考察を行いました.正直Scene Colorの挙動については未だよく分かっていませんが,OpenColorIOのおかげでRaw情報は取り出すことができそうなのでとりあえずはこれで対処したいと思います.色って複雑!