【HDRと色彩管理5】ゲームにおけるHDR

最後のセクションは初心に戻ります。ゲームでHDR表示の使用を紹介します。2016年以降、主要なゲームメーカーは、自社のゲームでHDR表示をサポートし始めています。初期のin FAMOUSシリーズからUE4で開発されたGears of War 5まで、市場に出回っている3Aゲームにおいて、HDR表示へのサポートは不可欠になっています。このセクションは、まずCOD共有の実現方法を確認し、そしてUE4のHDRへの実現と比較します。


---
5.1HDR in COD：WW II
--


ゲームのHDR表示は、ACESの多くの概念と実現、特にOutput Transform（RRT + ODT）の部分を適用しています。ただし、映画やテレビのプロセスと違い、ACESの概念の多くは実際には必要ありません。たとえば、ゲームは複雑なIDTを考慮する必要はあまりありません。基本的に、sRGBテクスチャの入Output Transformで十分です。 COD：WW IIも、ACESに基づいて独自のHDRカラーパイプラインに独自の調整を加えました。



[CODの技術的な共有](https://research.activision.com/publications/archives/hdr-in-call-of-duty)において、著者はHDR表示の実現目標は次のとおりであると述べました。

lHDRとSDRの視覚的な一貫性を維持すること

l新しいSDRパイプラインでは、元の古いSDRと同じ効果を得ることができること

lアーティストに迷惑を掛けないように、高品質で効率的なパフォーマンスがあること



5.1.1新しいカラーパイプライン
---


この目標を念頭に置いて、COD：WWIIの新しいカラーパイプラインは次のようになります。


![1.1593853882202.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/80589809-55d8-2518-73b8-d1ae17e65138.png)

Source: HDR in Call of Duty

CODの以前のパイプラインと比較して、新しいHDRパイプラインにはいくつかの重要な変更があります。

l後処理は、HDRとSDRの視覚的な一貫性を維持するために、元のsRGB gamma空間からHDR linear空間に移動された

l次に、露出パラメータを適用し、シーン全体の輝度をスケーリングして、Exposed HDR空間に入る。この空間は依然として線形空間であるが、同時に色変換マトリックスを使用して、色空間をsRGBからRec.2020色空間に変換する

lRec.2020線形色空間でColor Gradingを適用する

Tonemappingを適用する。 Tonemappingは現在2つの操作に分かれている。まず、固定のTone Curveを使用して統一的な画像変換（ACESのRRTに相当）を実行し、次にdisplay mappingを使用して特定の出力デバイス（ACESのODTに相当）に出力します。 ）。これらの2つのステップは、ACESのOutput Transform、つまりRRT+ODTに相当します。



5.1.2 Tone Curve
---


ACESのRRT段階と同等で、著者は固定のTone Curveを使用して、HDRシーンの輝度をPQに対応する0〜10000nitの出力輝度範囲に再マッピングします。これは実にSカーブです。このSカーブの定義は、欲しい芸術的表示に決められます。このようなSカーブに依存すると、古いSDRでのfilmic tonemapと同様の効果を得ることができます。以下は、Tone Curveを適用する前後の写真の対照です。適用後の画面は、より「映画的」になっていることがわかります。


![2.1593854291522.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f1644dfc-cee3-d933-a74e-e1900494849b.png)

Source: HDR in Call of Duty



このSカーブを以下に示します。水平座標と垂直座標は、輝度のlog（対数）を取った後のstop値であり、その入力色空間はACEScg、つまりAP1空間です。


![3.1593854330259.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e9b35490-4f00-9f51-071c-4ff10e52f8f4.png)

Source: HDR in Call of Duty

CODで使用されるこの曲線は、ネイティブ[ACES RRT](https://github.com/ampas/aces-dev/blob/master/transforms/ctl/rrt/RRT.ctl)で使用されるtonescale曲線に非常に類似しています。このRR Ttonescale曲線自体は、セグメント化されたB-spine曲線によって定義されます（詳細については、[ACESlib.Tonescales.ctlファイル](https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.Tonescales.ctl)のsegmented_spline_c5_fwd関数を参照してください）。ACESRRTの完全なワークフローを以下に示します。


![4.1593855106646.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ae80cae3-cade-7e44-97e0-60877cd5bbab.png)

ここまで、すべてのスタイルと効果の計算が行われ、シーン全体の輝度が0〜10,000ニットの範囲に再マッピングされています。これで、この出力輝度範囲内で実際に画像を出力できます。

5.1.3 Display Mapping
---


Display Mappingは、特定の出力デバイスに関連する後続の表示ロジックを引き継ぎます。これには、次の主な操作が含まれます。

l HDR Range Reduction：0〜10000 nitの輝度範囲は、市販の表示（ディスプレイ）デバイスには大きすぎるため、出力デバイスの輝度範囲を縮小するには、次の操作が必要になります。

l Color Space Transformation：色変換を使用して、色空間を出力デバイスの色空間に変換します



Display Mappingは、SDRとHDR表示の2つのラインに分けることができます。まず、HDR表示でのDisplay Mappingを見てみましょう。その2つの部分は次のとおりです。

lBT. 2390EETFとユーザーによる校正（キャリブレーション）でHDR Range Reductionを計算します

lPQ OETFを使用して、結果を出力デバイスの色空間に変換します



1つ目はHDR Range Reductionです。このステップの目的は、0〜10000ニットをユーザーのHDR表示デバイスの実際の黒点と白点の範囲にマッピングすることです。著者は、このマッピングを実現するためにBT.2390のEETF伝達関数を使用することを選択しました。BT.2390のEETFは、指定された最小および最大輝度値に従って、Hermite曲線をICtCp空間の輝度値に適用することにより、出力輝度範囲を調整できます。以下は、さまざまな最大輝度値と最小輝度値が与えられた場合の関数曲線です。


![5.1593855078438.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3d9bdd6c-f9b4-5ec9-a74b-d9535a3da53d.png)

Source: HDR in Call of Duty



BT2390のEETF入力、最小および最大輝度値は、校正機能を表示する形でユーザーによって設定されています。 HDR白色点の設定インターフェースは次のとおりです。




![6.1593855175652.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/25430e8d-b6a5-53e9-bba5-7ddc71ecc7c4.png)

Source: HDR in Call of Duty


HDR黒色点の設定は次のとおりです。



![7.1593855213524.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/65d8caeb-bf61-8edc-7ff1-405527585c3b.png)
Source: HDR in Call of Duty



HDR Range Reductionを完了した後、最後のステップは、PQ OETFを直接使用して最終出力信号をエンコードします。



SDR表示でのDisplay Mappingは、もう1つのロジックです。 その2つの部分は次のとおりです。

l固定曲線を使用してSDR Range Reductionを計算します

l色変換行列を使用して色空間をBT.2020からBT.709に変換し、BT.1886 OETFを使用して最終結果を出力デバイスの色空間に変換します。



著者は、このマッピング曲線として、線形関数セグメント部分+指数shoulderのfalloff部分を使用します。


![8.1593855329797.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/9699aff8-d436-335f-0b09-cb266a10755d.png)

Source: HDR in Call of Duty

RGBチャンネルに異なる曲線のマッピングを適用することにより、色相のシフトが発生します。この問題を解決するために、著者は純粋な輝度曲線に対して同じ曲線マッピングを個別に実行して、色調の偏りのない結果を取得してから、2つの結果の間を補間します。SDRも以前と同様のユーザー校正パラメーターを使用しますが、黒色点の調整のみに用いられています。 最後に、BT.1886 OETFを使用して出力信号のエンコードを実行します。



COD：WW IIのこのDisplay Mappingステージは、ネイティブのACESODTステージと非常によく似ています。 ACES ODTにはRange ReductionおよびEOTFエンコード信号の適用と同等のステージもあります。ACES ODTの完全なワークフロー（例として[P3D60](https://github.com/ampas/aces-dev/blob/master/transforms/ctl/odt/p3/ODT.Academy.P3D65_48nits.ctl)を取り上げます）を以下に示します。

![9.1593855456284.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/6e84b3fa-55c4-314e-cbac-980962495302.png)


上の図のODT tonescaleは、CODでのRange Reductionと同等ですが、その実現はCODとは異なります。このODTtonescale曲線は、セグメント化されたB-spine曲線によっても定義されます（詳細については、[ACESlib.Tonescales.ctlファイル](https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.Tonescales.ctl)のsegmented_spline_c9_fwd関数を参照してください）。

5.1.4AAとUI
---


Temporal AAは、display mappingの後に実行されるため、AAは視覚的な線形空間で計算でき、追加の色空間変換やパラメーター調整を回避できます。



最後のステップは、UIをレンダリングすることです。 UIをレンダリングする方法は2つあります。

l1つは、最初にUIをoffscreen bufferにレンダリングし、次にそれを最終的な画像に合成することです。

lもう1つは、UIをbackbufferに直接レンダリングすることです。



COD：WW IIは2番目の方法を選択しました。ここでもSDRとHDRの2つのロジックラインに分割することもできます。

lSDR出力パスでは、ロジックは古いパイプラインと一致しています。つまり、sRGB色空間に直接混合させます。

lHDR出力パスでは、UIの輝度値が最大300 nitの範囲にスケーリングされ、色空間変換をBT. 2020色空間に変換してから、PQ曲線マッピングを適用します。



上記の2つのパスの結果は完全に同じではありませんが、作者にとっては十分に近いものです。さらに、Alpha Blend混合モードと比較して、Additive混合モードには、追加のfixが必要となってシェーダーの計算を増やされました。幸いに、WW IIのUIには通常の半透明の混合モードで十分です。

5.1.5 CLUT
---


実現に関しては、CODはUniversal CLUTと呼ばれるLUTを使用して、上記のパイプラインの色変換操作を完了します。これにより、パイプライン全体のパフォーマンスを大幅に向上させることができます。


![10.1593855860118.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a1cdcea9-bc89-e1e5-1f57-81dbbd28122e.png)

Source: HDR in Call of Duty



このCLUT（Color LUT）の入力は、露出パラメータによってスケーリングされた線形シーンの色値であり、出力はoutput-referredの色値です。 このCLUTには、次の色彩操作が含まれています。

HDR Color Grading
HDR Tone Curve（RRT）
Display Mapping（ODT）
CLUTは本質的に3Dテクスチャであり、各フレーム描画の初期段階でasync compute shaderによって計算されます。著者は、log2関数を使用して、入力された露後のHDR色値をテクスチャ座標空間の0〜1の範囲に変換します。これに基づいて、この3Dテクスチャで変換された色値を検索できます。



5.2UE4におけるHDR
--


Talk is cheap, show me the code. CODのソースコードはありませんが、UE4があります！ 次に、これまでに学んだすべての理論的知識を組み合わせて、HDR表示がUE4にどのように実現されているかを確認します。



一般に、UE4におけるHDRカラーパイプラインはWW IIのロジックとほぼ同じですが、一部の細部はWW IIのサポートほど良くありません。 それを段階的に見ていきましょう。



5.2.1 How to Use使用方法
---


UE4でHDR表示を使用するのは非常に簡単です。UE4のドキュメントには、詳しい説明があります。一般的に、次の3つのコマンドラインパラメータを使用できます。

lr.HDR.EnableHDROutput：HDR出力を有効にするかどうかを制御します。主にDXGI設定を制御して、backbufferの形式を変更し、HDR形式のbackbufferをHDR表示デバイスに送信できるようにします。

lr.HDR.Display.OutputDevice：使用する出力デバイスを制御する伝達関数。 UE4にサポートされています。

lr.HDR.Display.ColorGamut：出力デバイスの色空間として使用される色空間を制御します。



また、UIレンダリングに関連する2つの調整パラメーターがあります。

lr.HDR.UI.CompositeMode：UIが有効にしているかどうかを制御するHDRコンポジットモードで、SDRと同じUI視覚効果を取得しようとしています。

lr.HDR.UI.Level：UI合成のハイライト値を制御します。



r.HDR.EnableHDROutputコンソールパラメーターを除いて、他のパラメーターは、UE4のレンダリングロジックを制御するshaderパラメーターです。以前の開発プロセスでは、r.HDR.EnableHDROutputによってプログラムが簡単にクラッシュしたり、HDRが正常にアクティブ化されなかったりする可能性がありました。通常の状況では、r.HDR.EnableHDROutputを1に設定すると、UE4は、現在のプラットフォームでHDRを有効にしようとします。D3D12を例として、以下のようになります。

![11.1593856296220.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b584426c-39a8-ef6f-041f-62b81ebb5f55.png)


上のSetHDRTVModeおよびEnsureColorSpaceは、現在のコマンドラインパラメーターに従って対応するDXGIパラメーターを設定します。SetHDRTVMode関数は現在のr.HDR.Display.OutputDeviceおよびr.HDR.Displayのパラメータに従って、HDR metaデータに対応する色空間の三原色、白色点の値、および出力の最大および最小輝度値を設定する役割を果たします。これにより、対応するHDR metaデータが現在のHDR表示デバイスに送信されます。

![12.1593856318909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/cb4b5525-ac5a-85b5-3dae-ad3e89b6ff1b.png)


EnsureColorSpace関数は、正しいbackbuffer色空間形式を設定する役割を果たします。

![13.1593856348707.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/5901053a-10c4-3a4f-6409-bc411e1270be.png)


ユーザーが出力デバイスの最小および最大輝度値を指定できるCOD：WW IIとは異なり、UE4は現在1000または2000 nitの固定最大輝度値のみをサポートしています。つまり、UE4のODT実現はWWIIとは異なります。UE4のロジックはより簡単になります。これについては後で詳しく説明します。



ここまで、表示デバイスに関連するDXGI設定は完了しており、残りのロジックはレンダリングパイプラインレベルにあります。r.HDR.Display.OutputDeviceは、出力デバイスのタイプを制御します。これは、ODTステージで使用されるOETF伝達関数などのロジックに影響を与えます。 UE4は現在、合計7種類の出力デバイスをサポートしています。

![14.1593856407545.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d12678b9-e3cc-77da-bd92-c31be6f810ba.png)


このうち、r.HDR.Display.OutputDevice≥3の部分はHDR出力デバイスに対応し、残りはSDR出力デバイスに対応します。コードでも、この値でSDRロジックまたはHDRロジックが実行されているかを判定します。

![15.1593856520535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/541a54a9-c8ff-2a1f-bf85-4f654cf76493.png)


r.HDR.Display.ColorGamutは、出力デバイスの色空間を制御します。これは、ODTステージで使用される色空間変換などのロジックに影響します。 UE4は現在、5種類の表示色空間をサポートしています。



その中で、r.HDR.Display.ColorGamut≤1の部分はSDR色空間に対応し、残りはHDR色空間に対応します。



これらの基本的なパラメータを理解した後、具体的なコードロジック部分を見てみましょう。

5.2.2 HDR Scene Rendering
---


この部分のロジックは一般的で、通常はsRGBテクスチャを入力して、物理の照明レンダリングを行い、シーン輝度が幅広いHDRシーン色値を取得できます。ピーク輝度は数千ニットにもなる可能性があります。次のステージに出力します。



この部分の色空間はsRGB線形空間です。



5.2.3 HDR Post Processing
---


UE4の後処理は、AAを含むHDR線形空間で計算されます。実際、UE4のAAは、DOFの直後、後処理の非常に早い段階で発生します。 以下は、PCDX11でのUE4.25のRenderdocスクリーンショットです。

![16.1593856630451.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3b36f3ca-eb67-6c80-d6a8-3ab841fb6065.png)


 

5.2.4 Generating CLUT



まずは、現在フレームのCLUTを生成します。この部分のロジックは、PostProcessCombineLUTs.usfで処理されます。現在のプラットフォームに応じて、PSとCSと二つのバージョンを選択できます。このCLUTには、次の色操作が含まれています。

White Balance
HDR Color Grading
Output Transform（RRT + ODT）


**（1）テクスチャ座標を線形色値に変換する**



コードは最初に、現在のLUTの座標をその位置に対応する線形シーンの色に変換します。
```

float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

// construct the neutral color from a 3d position volume texture        

float4 Neutral;

{         

float2 UV = InUV - float2(0.5f / LUTSize, 0.5f / LUTSize);          

Neutral = float4(UV * LUTSize / (LUTSize - 1), InLayerIndex / (LUTSize - 1), 0);

}     

...     

float3 LUTEncodedColor = Neutral.rgb;     

float3 LinearColor;     

// Decode texture values as ST-2084 (Dolby PQ)     

if (GetOutputDevice() >= 3)     

{         

// Since ST2084 returns linear values in nits, divide by a scale factor to convert       

// the reference nit result to be 1.0 in linear.         

// (for efficiency multiply by precomputed inverse)         

LinearColor = ST2084ToLinear(LUTEncodedColor) * LinearToNitsScaleInverse;     

}     

// Decode log values     

else         

LinearColor = LogToLin( LUTEncodedColor ) - LogToLin( 0 );
```

ここには2つのブランチが含まれます。HDR出力ロジックに対応するOutputDevice（つまり、r.HDR.Display.OutputDevice）> = 3の場合、前の記事で説明したST 2084/PQ伝達関数を使用して0〜1の範囲は、0〜100ニットの変換に再マッピングされます。スケーリングにはLinearToNitsScaleInverse値が使用されることに注意してください。これは、PQ伝達関数は入力信号1を10000 nitにマッピングできるため、PQエンコーディングのLUTスペースをより有効に活用するためのものです。それに、露出スケーリング後のシーンの輝度値を大幅に越しました。つまり、シーンの最大輝度値が約100 nitの場合、LUT全体の有効なコーディング部分は50％未満しか占めないため、LUTスペースの無駄になります。UE4は、スケーリング値として100を使用することを選択したため、100nitは座標1のLUTのピクセル部分に対応します。

```
// Scale factor for converting pixel values to nits. 

// This value is required for PQ (ST2084) conversions, because PQ linear values are in nits. 

// The purpose is to make good use of PQ lut entries. A scale factor of 100 conveniently places 

// about half of the PQ lut indexing below 1.0, with the other half for input values over 1.0. // Also, 100nits is the expected monitor brightness for a 1.0 pixel value without a tone curve. static const float LinearToNitsScale = 100.0; static const float LinearToNitsScaleInverse = 1.0 / 100.0;
```

SDR出力デバイスブランチは、logを使用しエンコードします。log関数を使用して、0〜1の範囲を0からシーンの最大ピクセル値（約50）への変換に再マップします。


```
float3 LogToLin( float3 LogColor )

{     

const float LinearRange = 14;     

const float LinearGrey = 0.18;     

const float ExposureGrey = 444;      

// Using stripped down, 'pure log', formula. Parameterized by grey points and dynamic range covered.     

float3 LinearColor = exp2( ( LogColor - ExposureGrey / 1023.0 ) * LinearRange ) * LinearGrey;     

//float3 LinearColor = 2 * ( pow(10.0, ((LogColor - 0.616596 - 0.03) / 0.432699)) - 0.037584 ); // SLog     

//float3 LinearColor = ( pow( 10, ( 1023 * LogColor - 685 ) / 300) - .0108 ) / (1 - .0108); // Cineon     

//LinearColor = max( 0, LinearColor );      



return LinearColor;

}
```

**（2）Implement White Balance**



上に変換された線形シーン値でWhite Balanceを計算します。

```
float4 CombineLUTsCommon（float2 InUV、uint InLayerIndex）

{{

..。

float3 BalancedColor = WhiteBalance（LinearColor）;
```

**（3）sRGBからAP1色空間への変換**



White Balanceを計算した後、色空間はsRGBLinearからACESAP1線形色空間に変換されます。

```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

float3 ColorAP1 = mul( sRGB_2_AP1, BalancedColor );
```

前回の記事で述べたことを思い出してください。AP1色空間の色域範囲はRec. 2020に非常に近く、CGおよびVFX計算に適し、得られた結果は、スペクトルレンダリングのground truth結果に近くなっています。



色域をsRGB空間から広色域AP1に変換しましたが、前のシーンのレンダリングはsRGB線形色空間で実行されたため、取得した色域値は常にsRGB色域の範囲内でした。ここでUE4にはtrickがあり、シーンはWide Color Spaceで計算されているように見えます。その三原色はP3とAP1の間にあり、Wide_2_AP1を使用して色変換を行い、最後にパラメーターと元のsRGB_2_AP1の変換結果を補間します。


```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     // Expand bright saturated colors outside the sRGB gamut to fake wide gamut rendering.     

if (!bUseMobileTonemapper)     

{         

float  LumaAP1 = dot( ColorAP1, AP1_RGB2Y );         

float3 ChromaAP1 = ColorAP1 / LumaAP1;          



float ChromaDistSqr = dot( ChromaAP1 - 1, ChromaAP1 - 1 );         

float ExpandAmount = ( 1 - exp2( -4 * ChromaDistSqr ) ) * ( 1 - exp2( -4 * ExpandGamut * LumaAP1*LumaAP1 ) );          

// Bizarre matrix but this expands sRGB to between P3 and AP1         

// CIE 1931 chromaticities: x       y         

//              Red:        0.6965  0.3065         

//              Green:      0.245   0.718         

//              Blue:       0.1302  0.0456         

//              White:      0.3127  0.329         

const float3x3 Wide_2_XYZ_MAT =          

{             

0.5441691,  0.2395926,  0.1666943,             

0.2394656,  0.7021530,  0.0583814,             

-0.0023439,  0.0361834,  1.0552183,         

};          



const float3x3 Wide_2_AP1 = mul( XYZ_2_AP1_MAT, Wide_2_XYZ_MAT );         

const float3x3 ExpandMat = mul( Wide_2_AP1, AP1_2_sRGB );          



float3 ColorExpand = mul( ExpandMat, ColorAP1 );         

ColorAP1 = lerp( ColorAP1, ColorExpand, ExpandAmount );     

}
補間に使用されるパラメータは、後処理コンポーネントのExpand Gamutによって制御されます。デフォルトは1です。

![18.1593859480302.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/05a5b620-8ac5-609f-d33c-0b2b2b36c79b.png)


 

（4）Implement Color Grading



次に、AP1空間でColor Gradingを計算します。

float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...      

ColorAP1 = ColorCorrectAll( ColorAP1 );
```

UE4のColor Gradingパラメータは豊富です。一般的に、定型化された色相に関連する調整は、Color Gradingで行うことをお勧めします。 Color GradingまたはColor Correctionは、アーティストがシーンごとに照明の色相を調整する方法を提供します。UE4は、Post Process Volumeに色補正機能と同様の多数のパラメータを提供します。




![19.1593859512304.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/303e9269-6ccc-27bf-f313-4d433e449db3.png)



これまでのところ、アーティストスタイルに関連するすべての計算と調整が完了しています。次のステップは、出力デバイスに応じてOutput Transformを計算することです。この部分では、ACESの実現ロジックを多く使用します。これは、SDRとHDRの2つのロージックパスに分けることができます。



**（5）SDRでのOutput Transform**



SDRでのOutput Transformは、ACESのLMT + RRT+ODT変換を使用します。 1つ目はLMTの部分です。UE4はACESLMTのBlueLightArtifact Fix部分を使用して、高輝度の青の値によって引き起こされる過飽和の問題を修正します。

```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

const float3x3 BlueCorrect =     

{         

0.9404372683, -0.0183068787, 0.0778696104,         

0.0083786969,  0.8286599939, 0.1629613092,         

0.0005471261, -0.0008833746, 1.0003362486     

};     

const float3x3 BlueCorrectInv =     

{         

1.06318,     0.0233956, -0.0865726,         

-0.0106337,   1.20632,   -0.19569,         

-0.000590887, 0.00105248, 0.999538     

};     

const float3x3 BlueCorrectAP1    = mul( AP0_2_AP1, mul( BlueCorrect,    AP1_2_AP0 ) );     

const float3x3 BlueCorrectInvAP1 = mul( AP0_2_AP1, mul( BlueCorrectInv, AP1_2_AP0 ) );      

// Blue correction     

ColorAP1 = lerp( ColorAP1, mul( BlueCorrectAP1, ColorAP1 ), BlueCorrection );
```

この部分は特に説明するところはありません、「標準」と言えます。補正度パラメータは、後処理コンポーネントのBlue Correction値によって制御でき、デフォルト値は0.6です。



次に、AP1空間でTonemappingを計算します。


```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{    

...     

// Tonemapped color in the AP1 gamut     

ColorAP1 = FilmToneMap( ColorAP1 );
```

Tonemappingの具体的な意味は、エンジンやテクノロジーの共有によってわずかに異なることに注意してください。本質的な違いはありませんが、混乱を避けるために、もう少し説明します。 COD：WW IIの共有では、TonemappingにはTone Curve（RRT）とDisplay Mapping（ODT）の操作が含まれます。つまり、実際には2つのS曲線が含まれています。 UE4では、Tnemappingとは、RRTとODTを組み合わせた後の統合曲線マッピング部分を指します。つまり、S曲線は、Output Transformステージで直接再マッピングするために使用され、RRTステージとODTステージは厳密に区別されなくなりました。 UE4のTonemappingロジックはFilmToneMap関数で処理されます。この部分コードは比較的長く、コッピーしないようにします。要約すると、ACES RRTステージとACES SDR ODTステージの実現ロジックを組み合わせたものです。



UE4では、Post Process VolumeのTonemapperでこのS曲線パラメータを調整できます。これらのパラメータのデフォルト値はACESの実現と一致しているため、特別な要求がない場合は、次のパラメータをシーンやレンズごとに調整しないでください。Color Gradingパラメータを使用してすべての芸術スタイルを制御します。



最後のステップは、OETF伝達関数を適用してエンコードします。その前に、白色点の精度を確保するために、最初に前にLMTによって実行されたBlue Correction操作をオフセットし、次に色空間をsRGB色空間に再変換して、最終出力の準備をします。

```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

// Uncorrect blue to maintain white point     

ColorAP1 = lerp( ColorAP1, mul( BlueCorrectInvAP1, ColorAP1 ), BlueCorrection );   // Convert from AP1 to sRGB and clip out-of-gamut values     

float3 FilmColor = max(0, mul( AP1_2_sRGB, ColorAP1 ));
```

説明の便宜上、SDR出力デバイスに関連するOETF計算をまとめました。まとめて説明してみると、出力デバイスに応じて異なるOETF伝達関数を選択します。


```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

half3 OutDeviceColor = 0;     

// sRGB, user specified gamut     

if( GetOutputDevice() == 0 )     

{                

// Convert from sRGB to specified output gamut           

float3 OutputGamutColor = mul( AP1_2_Output, mul( sRGB_2_AP1, FilmColor ) );          

// Apply conversion to sRGB (this must be an exact sRGB conversion else darks are bad).         

OutDeviceColor = LinearToSrgb( OutputGamutColor );     

}     

// Rec 709, user specified gamut     

else if( GetOutputDevice() == 1 )     

{         

// Convert from sRGB to specified output gamut         

float3 OutputGamutColor = mul( AP1_2_Output, mul( sRGB_2_AP1, FilmColor ) );          

// Didn't profile yet if the branching version would be faster (different linear segment).         

OutDeviceColor = LinearTo709Branchless( OutputGamutColor );     

}     

// OutputDevice == 2     

// Gamma 2.2, user specified gamut     

else if ( GetOutputDevice() == 2 )     

{         

// Convert from sRGB to specified output gamut         

float3 OutputGamutColor = mul( AP1_2_Output, mul( sRGB_2_AP1, FilmColor ) );          

// This is different than the prior "gamma" curve adjustment (but reusing the variable).         

// For displays set to a gamma colorspace.         

// Note, MacOSX native output is raw gamma 2.2 not sRGB!         

OutDeviceColor = pow( OutputGamutColor, InverseGamma.z );     

}          



// Better to saturate(lerp(a,b,t)) than lerp(saturate(a),saturate(b),t)     

OutColor.rgb = OutDeviceColor / 1.05;     

OutColor.a = 0;      

return OutColor;

}
```

さまざまなOETFによってエンコードされた情報は、対応するSDR表示デバイスに送信する準備ができています。



**###HDRでのOutput Transform**



SDR Output Transformと比較して、UE4のHDROutput Transformは変換がはるかに少なくなっています。Tonemappingと同様の計算は、UE4のHDR出力パイプラインでは実現されませんが、Color Gradingが計算された後、色空間はAP1からsRGBに直接再変換されます。


```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

ColorAP1 = ColorCorrectAll( ColorAP1 );      



// Store for Legacy tonemap later and for Linear HDR output without tone curve    

float3 GradedColor = mul( AP1_2_sRGB, ColorAP1 );
```

そのOutput Transformは基本的に直接的にODT変換を行い、その本質は、出力信号をエンコードするためにさまざまなOETF関数を使用することでもあります。


```
float4 CombineLUTsCommon(float2 InUV, uint InLayerIndex)

{     

...     

if( GetOutputDevice() == 3 || GetOutputDevice() == 5 )     

{                

// 1000 nit ODT         

float3 ODTColor = ACESOutputTransforms1000( GradedColor );          



// Convert from AP1 to specified output gamut         

ODTColor = mul( AP1_2_Output, ODTColor );          



// Apply conversion to ST-2084 (Dolby PQ)         

OutDeviceColor = LinearToST2084( ODTColor );     

}      

// ACES 2000nit transform with PQ/2084 encoding, user specified gamut      

else if( GetOutputDevice() == 4 || GetOutputDevice() == 6 )     

{                

// 2000 nit ODT         

float3 ODTColor = ACESOutputTransforms2000( GradedColor );          

// Convert from AP1 to specified output gamut         

ODTColor = mul( AP1_2_Output, ODTColor );          

// Apply conversion to ST-2084 (Dolby PQ)         

OutDeviceColor = LinearToST2084( ODTColor );     

}        

else if( GetOutputDevice() == 7 )     

{             

float3 OutputGamutColor = mul( AP1_2_Output, mul( sRGB_2_AP1, GradedColor ) );             

OutDeviceColor = LinearToST2084( OutputGamutColor );     

}          

// Better to saturate(lerp(a,b,t)) than lerp(saturate(a),saturate(b),t)     

OutColor.rgb = OutDeviceColor / 1.05;     

OutColor.a = 0;      



return OutColor;

}
```

最後に、エンコードされたHDR信号をHDR表示デバイスに渡すために、PQのOETF伝達関数を使用してエンコードします。



**##CLUTを使用する**



CLUTを使用するロジックは、PostProcessTonemap.usfで処理され、その主な関数ロジックはTonemapCommonPS関数にあります。 TonemapCommonPS関数は、最初に、Grain、Color Fringe、Sharpen、Bloom、Exposure、Vignetteなど、これまでに完了していない後処理を計算し、上記の後処理計算で得られた最終結果を3D LUTのサンプリング座標に変換して色を検索します：


```
float4 TonemapCommonPS() {     // Compute Grain/Color Fringe/Sharpen/Bloom/Exposure/Vignette     ...     half3 OutDeviceColor = ColorLookupTable( LinearColor );     ...     return OutColor; }
```

ColorLookupTableの出力は、OETFによってエンコード後の信号値です。 ここまで、UE4のHDR出力パイプラインは終了しています。



##UE4におけるACES



UE4がBlue Light Artifact FixのLMT部分と、Fake Wide Gamutのtrick部分を除いて、SDRでの実現はACES sRGBとほぼ同じです。これにより、DCCソフトウェアでUE4レンダリング効果を調整するパスが提供されます。完全にカスタムのOCIOファイルまたはその他のLUTファイルを実現したくない場合は、UE4でこれら2つの変更をオフにすることができ（後処理コンポーネントでBlue CorrectionとExpand GamutGamutの値を0に設定する）、そしてACES RRT +sRGBODTに関連するOCIOを使用します。



＃UE4とCODのHDR実現の比較



次のように、UE4とCOD がHDR表示パイプラインでの実現とはまだ多少異なることがわかります。

WW IIのOutput Transformは、UE4の処理よりも複雑です。

n要するに、UE4はACESのネイティブコードロジックを直接使用し、SDRパスの下でACES RRTとODTのTonemapping計算を結合し、UE4のレンダリングパイプラインにネストします。ただし、HDRパスでは、ACESの最も基本的なOETF部分のみが保持され、1000ニットと2000ニットの2つの固定最大輝度値のみがサポートされます。つまり、HDRをオンにした後、手動でHDR Tonemappingを調整する必要があります。これ限り、SDRと同じ画像効果を得られます。



nWW IIは彼らのニーズに合わせてより多くの適応作業を行いましたが、それでも明確なRRTおよびODTステップを保持していました。RRTステージでは、ACES RRTと非常によく似たTone Curveを使用して調整するため、マッピングされたシーンの輝度はPQの0〜10000ニットの範囲になります。ODTステージでは、ACES ODTと同様のDisplay Mapping計算も保持されます。つまり、アプリケーションデバイスのOETFの前に、WW IIはSDRとHDRの下で相互に独立したS曲線を使用して、特別なHDR Range Reductionを実行します。このプロセスにより、ユーザーは、さまざまな表示デバイスの表示パラメーター（最小および最大輝度値など）を処理するためのパラメーターの調整に参加し、より優れた制御性を実現できます。



lWW IIのUI描画はbackbufferーに直接的に的に混合されますが、UE4は最初にoffscreen bufferに出力され、次にbackbufferーに混合されます



まとめてみると、UE4の現在の実現は、私たちに基盤を与えています。より良いHDR表示を実現するには、いくつかの二次開発が必要です。たとえば、UE4で開発されたGears of War 5のHDR表示は、ネイティブUE4と比較して、より優れています。Gears of War 5の[インタビュー](https://www.eurogamer.net/digitalfoundry-2019-gears-5-tech-interview)により、Tonemapping を使用していることを明らかにしました。それは、Microsoftのファーストパーティゲームで多数のHDR / SDR画像をトレーニングサンプルとして使用し、機械学習を使用してトレーニングして得られたものです。その上に、ゲーマーが表示デバイスに応じてゲーム内に校正することをサーポットします。



おわりに
--


HDR表示に関してはACESが常に言及されていますが、ACESはゲームのHDR表示のいわゆる「銀の弾丸」ではないことに徐々に気づきました。仕事でTonemappingを選択する方法にも出会ったとき、AngeloPesceは[彼のブログ](http://c0de517e.blogspot.com/2017/02/tonemapping-on-hdr-displays-aces-to.html)でそのような質問をしました。 ACESTonemappingは本当にすべてのゲームに適していますか？ ACESのSカーブのさまざまなパラメータは、多くの映画やテレビ業界の大物によって承認されていますが、これは、多数の画像の検証後に取得した「最も合理的な」パラメータですが、この「高度なフィルムとテレビセンス」はすべてのゲームに適しているわけではありません。視覚的な「映画感」を除いて、ACESのもう1つの大きな利点は標準化ですが、これがゲーム業界でそれほど重要であるかどうかは[議論](http://c0de517e.blogspot.com/2017/02/tonemapping-on-hdr-displays-aces-to.html)の余地があるようです。 2Dスタイルのゲームなど、一部のゲームタイプでは、開発ツールの種類が限られており、映画やテレビ業界と同じ標準を使用する必要はまったくないようです。しかし、「標準化」が開発者に安心感を与えることは否定できません。また、小さなワークショップからの工業化の重要な兆候でもあります。ACES標準に適応するか、独自の標準を開発するかが問題です。



UnityとUE4の両方に現在完全なACESが組み込まれていますが、HDR表示をサポートする3Aゲームの共有を見てみると、Gears of War 5使用された[ML Tonemapping](https://www.eurogamer.net/digitalfoundry-2019-gears-5-tech-interview) 、WW IIで使用されたBT2390EETF標準に基づくHDRDisplay Mappingなど、ACESのアイデアに独自の変更が加えられていることがわかります。これらの商用エンジンでの実現は、私たちに出発点を与えるだけであると言えます。PBRタイプのゲームの場合、ACESは他のDCCソフトウェアとの互換性が高くなります。しかし、現在の国内開発環境では、HDR表示は重要な位置に昇格していません。同時に、完全なACES実現は、携帯電話プラットフォームに多くの計算圧力をかけます。 簡略化されたPBRはACES標準化の重要性を弱めました。それに、柔軟性の欠如と、多数の主観的な定型化された画面開発のニーズが相まって、簡略化されたACES [SDR Tonemapping](https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/) や[HDR Tonemapping](https://knarkowicz.wordpress.com/2016/08/31/hdr-display-first-steps/)など、他の[非ACESTonemapping](https://bartwronski.com/2016/08/29/localized-tonemapping/)が現在の国内ゲーム開発により適しているようです。

---
**Reference**
1.[Digital Dragons 2018: HDR in Call of Duty](https://research.activision.com/publications/archives/hdr-in-call-of-duty)
2.[SIGGRAPH Asia 2018：Practical HDR and Wide Color Techniques in Gran Turismo SPORT](https://www.polyphony.co.jp/publications/sa2018/)
3.[GDC 2019: Not-So-Little Light: Bringing 'Destiny 2' to HDR Displays](https://edu.uwa4d.com/lesson-detail/181/1009/0?isPreview=0)
4.[GDC 2018: Advances in the HDR Ecosystem](https://edu.uwa4d.com/lesson-detail/181/1009/0?isPreview=0)
5.[GDC 2017: HDR Dynamic Range Color Grading and Display in Frostbite](https://edu.uwa4d.com/lesson-detail/181/1009/0?isPreview=0)
6.https://www.resetera.com/threads/hdr-games-analysed.23587/
7.https://docs.unrealengine.com/en-US/Engine/Rendering/HDRDisplayOutput/index.html
8.https://developer.nvidia.com/hdr-ue4

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com