SDRとHDRで見た目を同じにするには
前回、HDRのディスプレイで、R10G10B10A2のフォーマットを描画することを試して、 とりあえず、R8G8B8A8よりも明るく表示されることは確認しました。
では、どのように調整することで、SDRとHDRの見た目を近づけることができるのか、試したいと思います。
まず、シンプルに、リニアカラーでスケーリングを設定できるようにPixelShaderを書きます。
struct PSInput {
float4 position : SV_POSITION;
float4 color : COLOR;
};
static const float3x3 Rec709ToRec2020Matrix = {
1.2249, -0.0420, -0.0197,
-0.0184, 1.2045, -0.0158,
-0.0051, -0.0249, 1.2796
};
// sRGBからリニアカラーへの変換
float3 SRGBToLinear(float3 color) {
return (color <= 0.04045) ? (color / 12.92) : pow((color + 0.055) / 1.055, 2.4);
}
// PQ補正関数 (ST 2084, 簡略版)
float3 PQCorrect(float3 color) {
// PQカーブの定数 (簡略版)
float m1 = 0.1593;
float m2 = 78.8438;
float c1 = 0.8359;
float c2 = 18.8516;
float c3 = 18.6875;
// PQカーブのエンコード式
return pow(max((c1 + c2 * pow(color, m1)) / (1.0 + c3 * pow(color, m1)), 0.0), m2);
}
float4 PSMain(PSInput input) : SV_TARGET {
// スケール
float scale = 0.01;
// sRGBをリニアに変換
float3 linearColor = SRGBToLinear(input.color.rgb) * scale;
// リニアカラーをRec. 2020に変換
float3 rec2020Color = mul(Rec709ToRec2020Matrix, linearColor);
// PQカーブ補正 (Rec. 2020)
float3 finalColor = PQCorrect(rec2020Color);
return float4(finalColor, input.color.a);
}
PQカーブ補正はSDRにおけるガンマ補正のようなものですかね。 リニアカラーの空間で、scaleをかけてみると、だいたい0.01倍にしたところで一致しました。
R10G10B10A2でリニアカラーに対して0.01のスケールを掛けたものがこちらです。
単純にいうと、100倍明るい?
そういえば、前回のスクリーンショットでは、HDRが色飛びしていたので、今回はWindows+Gキーを押して、キャプチャしました。
PQカーブは、広いダイナミックレンジ(最大10,000 nits)を扱うため、リニアではなく、人間の視覚に合わせた非線形なカーブになっています。
SDRは100nitsくらいなので、100倍くらい明るいものを表現できるのは正しいのでしょうか?
なお、本来はトーンマッピングで調整したりするのですが、ちょっと分かりにくかったので、シンプルなリニアなスケールにしました。
ちなみに、このディスプレイはHDR600ですから、6倍しか明るさを表現できません。
まだ勉強不足でこれが正しい結果なのかよく分かりません。もっと深く理解出来たら、続きを書きます。