見出し画像

Unreal Engine + OpenColorIOを使ってACEScgのリニアRGB値を取得する


目的

  • Unreal Engine (UE)の作業色空間であるACEScgの値に基づいたリニアRGBを取得できるようにしたい.

とりあえずの結論

・Unreal EngineのビューポートモードにOpenColorIOのプラグインを適用し,ACEScg to Rawでリニア色空間の色画像を表示
・OpenColorIOを適応したビューポートの状態でPixel Inspectorを使い,Final Colorの値で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"のプロジェクトをベースにしています.

実行環境
Engine: Unreal Engine 5.2.1
OS: Windows 11
CPU: Intel Corei7-13700
GPU: RTX 2060 super
RAM: 32 GB

実際に操作する前に今回のプロジェクトのWorking Color Spaceを決めます.Working Color Spaceとは上で述べたリニア色空間を何にするのかを設定するところです.今回はACES AP1 / ACEScgに設定します.

Edit->Project Setting->Rendering->Working Color Space

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オブジェクトを追加し,オブジェクトの設定以下のような設定にします.

Infinite Extend (Unbound)->Enabled
Lens->Exposure->Metering Mode->Manual
Lens->Exposure->Exposure Compensation->0.0
Apply Physical Camera Exposure->Disabled

リニアRGBの画像はちょっと暗い

UEでリニアRGBを表示する前に,リニアRGBの画像がどのように見えるのか紹介します.

GammaRGBとLinearRGBの見え方の違い

左が非線形のガンマ補正をかけたRGBで記録したもので,右がリニアRGBで記録したものです.画像自体は私が撮影したものですが,見た目に近いものは左の画像で,リニア画像はちょっと暗い印象です.なぜこのような現象が起きるのでしょうか?

ディスプレイ特性との関係

現象を簡単に説明するために上の図を用意しました.上の段から見てみましょう.一番左はディスプレイの特性を表しています.RGBに対して線形に光を制御できたら良いのですが,実はディスプレイは入力されたRGBに対して非線形な光を出しています.ガンマ特性とも言います.そのため,画像フォーマットの方で生のRaw RGB値に対して,ガンマ補正をかけ,わざと非線形なRGB値を作ります.このRGB値をディスプレイ入力することで,Raw RGB値に対して線形な光を制御できるようになります.

一方下段の方では,リニアなRGBはディスプレイのガンマ特性を直接表現するような形で画像を表示してしまいます.そのため,Raw RGB値に対して,中間層が思っているよりも暗い画像が表示されてしまうということになります.このあたりの現象もディスプレイのEIZOのページがわかりやすく解説していてくれています.

UEはデフォルトの状態でリニアRGBの画像を出力するのか?

実際にUEのデフォルトでWorking Color Spaceな画像を出力できるのか.調べてみると,ViewportのモードにScene Colorというモードが見つかりました.ドキュメントを確認してみると,

Scene Color はポストプロセスが完了する前にシーンの結果を表示します。つまり、露出、ブルーム、色補正、アンチ エイリアスの前という意味です。上図では、露出が明るくされていないため、シーンが非常に暗く表示されています。

Unreal Engine 5.2におけるドキュメントから

ポストプロセスというのは,計算されたリニアRGBに対して,ガンマ補正,トーンマッピング,色補正などディスプレイに表示するまでの様々な処理のことを指します.ということでScene Colorを表示してみましょう.

VIEW MODE->Buffer Visualization->Scene Color

Viewportのモードは上の図のように切り替えることができます.ちなみに今表示している画像はUEがあらかじめ設定したポストプロセスがかかった状態です.

Scene Colorモードを適応

これでScene Colorになりましたが,これをみた時,

あんまり暗くなってないぁ…

そう思いました.そうです.リニアRGBの画像を出しているはずのScene ColorがリニアRGBの挙動っぽくないのです.これでは本当にRGBの値として合っているのか不安です.若干長くなりましたが,この疑問が今回の調査のきっかけになっています.Scene Colorがあまり信用できなくなった今,どのようにしてリニアRGBの画像を取得できるようになるのでしょうか.

解決策: OpenColorIOプラグインの使用

カラーサイエンスに精通している人なら,「早くOpenColorIO使ってよ」と思っているでしょう.お待たせしました.OpenColorIOでUEのViewportの画像の変化を調べてみます.

OpenColorIOの一般的なワークフロー

OpenColorIOとは簡単に言えば統一的な色空間変換プラグインです.制作現場では様々な色空間のマテリアルを利用して,様々な色空間のディスプレイを利用してコンテンツを作っていた背景があり,そのため安定した色管理が困難でした.そこでOpenColorIOは,上の画像のように統一的な規格を作ることで安定して色管理を行うために作られたものです.UE5はこのOpenColorIOに対応しているのでこれを使ってリニア色空間の表示をしてみます.

OpenColorIOのプラグイン

UEのプラグインページに飛んで,OpenColorIOを検索してチェックボタンを押すとOpenColorIOのプラグインが有効になります.有効にしたら次にOpenColorIOを使う準備をします.

1. OCIOファイルのダウンロード

ACESが用意するOpenColorIOのConfigファイル

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のドキュメントに詳しく記載されてます.

OpenColorIO Configurationの作成,および設定

Content FolderでOpenColorIO Configurationを作成したら,上のように設定を行います.設定項目について,詳しくは下の通りです.

Configuration File:
OCIOのConfigファイル.色空間の情報が詰まっており,ACESが用意しているOCIOファイルを大抵使う.
Desired Color Spaces:
UEで使うであろう色空間を登録する.どの色空間を入れるかは個人の自由だが,ビューポートにOCIOを登録するなら作業色空間に採用されているものは登録する.今回の場合はACEScgのこと.
Desired Display-Views:
表示するディスプレイへの登録.ACEScgの色を変換する際,ディスプレイが表示できる色空間やガンマのよって変換方法が異なるために登録が必要.上の場合,sRGBのディスプレイを使うのでそれを軸に,SDR Video (HDRからSDRにトーンマップしたもの), Un-tone-mapped (トーンマップしない), Raw (ディスプレイの色空間への変換をしない)の3種類を追加している.

さてここでようやく準備できました.OpenColorIOをビューポートをに反映させてみましょう.

OpenColorIOをViewportに反映

ViewportにOpenColorIOを適応

OpenColorIOにプラグインを入れると,Viewportのモードが新しく追加されています.ここに先ほど作ったOpenColorIO Configurationを追加し,変換元 (上の方)にACEScg,変換先 (下の方)をsRGB - Display - Rawを追加します.Enable Displayにチェックを押します.

OpenColorIOの適応結果

よし!見事,OpenColorIOを適応することで,ACEcgのリニアRGBっぽい色を表示することができました.Scene Colorでは明るさが残ってたように見えますが,OpenColorIOを適応させることでリニアRGB特有の暗い画像を表示することができました.こうしてみると,やはりScene Colorはポストプロセス前のRGBとは言え,ACEScgのRawの情報ではないような気がします.

ACEScgのリニアRGB値の取得

ACEScgの画像を表示できたところで,実際の画像のRGB値を取得してみましょう.UEにはポインタ上のピクセルにおけるRGB値を調べるPixel Inspectorがあります.

Pixel Inspectorの見つけ方

このツールは,Tools->Debug->Pixel Inspectorから取得することができます.これを使って,カラーチェッカーのオブジェクトの色を抽出してみましょう.

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の見つけ方

ディスプレイガンマを変更するには,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への影響

ただ,気になることもあります.ディスプレイガンマを変更して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情報は取り出すことができそうなのでとりあえずはこれで対処したいと思います.色って複雑!

この記事が気に入ったらサポートをしてみませんか?