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倍にしたところで一致しました。

R8G8B8A8でそのまま表示したものがこれです。

R10G10B10A2でリニアカラーに対して0.01のスケールを掛けたものがこちらです。

単純にいうと、100倍明るい?

そういえば、前回のスクリーンショットでは、HDRが色飛びしていたので、今回はWindows+Gキーを押して、キャプチャしました。

https://enjiniacook.net/2024/02/20/windows-hdr%E6%98%A0%E5%83%8F%E3%81%AE%E3%82%B9%E3%82%AF%E3%82%B7%E3%83%A7%E6%96%B9%E6%B3%95%E3%81%8A%E3%82%88%E3%81%B3%E3%80%81twitter%E3%81%B8%E3%81%AE%E6%8A%95%E7%A8%BF%E6%96%B9%E6%B3%95/

PQカーブは、広いダイナミックレンジ(最大10,000 nits)を扱うため、リニアではなく、人間の視覚に合わせた非線形なカーブになっています。

SDRは100nitsくらいなので、100倍くらい明るいものを表現できるのは正しいのでしょうか?

なお、本来はトーンマッピングで調整したりするのですが、ちょっと分かりにくかったので、シンプルなリニアなスケールにしました。

ちなみに、このディスプレイはHDR600ですから、6倍しか明るさを表現できません。

まだ勉強不足でこれが正しい結果なのかよく分かりません。もっと深く理解出来たら、続きを書きます。