Direct3D12でHDR600のディスプレイに表示する

前回に続いて、ディスプレイ関連です。

前回も書きましたが、最近買ったこのディスプレイは、HDR600に対応しています。

Dell AW2724DM 27インチ Alienware ゲーミングモニター

HDR600の説明は以下の記事などが分かりやすいです。

https://chimolog.co/bto-gaming-monitor-vesa-hdr/

600という数値は明るさが600ニットということを表しています。(nit=cd/m2) 従来のSDRのディスプレイは100ニットくらいということで、かなり明るい画像を表現できるということになります。

Direct3D12で確かめてみます。

まず、フレームバッファのフォーマットを10ビットのRGBAに変更します。

//#define FRAME_BUFFER_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM)
#define FRAME_BUFFER_FORMAT (DXGI_FORMAT_R10G10B10A2_UNORM)

これを使って、SwapChainとRTVを作ります。

        swapChainDesc.Format = FRAME_BUFFER_FORMAT;
        psoDesc.RTVFormats[0] = FRAME_BUFFER_FORMAT;

他にもこの辺で使われています。

        UINT numModes = 0;
        pOutput->GetDisplayModeList(FRAME_BUFFER_FORMAT, 0, &numModes, nullptr);
        modeDesc.resize(numModes);
        pOutput->GetDisplayModeList(FRAME_BUFFER_FORMAT, 0, &numModes, &modeDesc[0]);
            swapChain->ResizeBuffers(FRAME_BUFFER_COUNT, width, height, FRAME_BUFFER_FORMAT, 0);
        ImGui_ImplDX12_Init(pDevice, FRAME_BUFFER_COUNT,
            FRAME_BUFFER_FORMAT, m_srvHeap.Get(),
            m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
            m_srvHeap->GetGPUDescriptorHandleForHeapStart());

これでフレームバッファはHDR用に設定できました。

次に、カラースペースを変更します。

        // カラースペース
        if (FRAME_BUFFER_FORMAT == DXGI_FORMAT_R10G10B10A2_UNORM)
        {
            swapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
        }
        else
        {
            swapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
        }

とりあえず、これだけでも表示できると思います。

DXGI_FORMAT_R8G8B8A8_UNORM を DXGI_FORMAT_R10G10B10A2_UNORM に切り替えて、三角形を1つ表示するアプリを実行してみました。

DXGI_FORMAT_R10G10B10A2_UNORM にすると、HDR対応のディスプレイは背景の白色がかなり明るくなりました。HDR 非対応のディスプレイは暗いというか、今まで見てきた白色です。

DXGI_FORMAT_R10G10B10A2_UNORM のスクリーンキャプチャです。

DXGI_FORMAT_R8G8B8A8_UNORM のスクリーンキャプチャです。

三角形のグラデーションが違いますね。( imgui の色もおかしくなっていますが、これは気にしないでください。) 上の画像のグラデーションが不自然に見えるのはトーンマップを経由していないからかもしれません。

背景の白色は違いがわかりませんね。目で見ると明らかな明るさの違いがあったのですが、画面をキャプチャしてもそれは伝わりません。

グラデーションの違いはカラースペースの違いだと思います。カラースペースはそのままに DXGI_FORMAT_R8G8B8A8_UNORM に変更してみたところ、 DXGI_FORMAT_R10G10B10A2_UNORM と同じグラデーションでした。

8ビットと10ビットの違いというのは、グラデーションが最大256段階なのか、最大1024段階なのか、という違いなのですが、今回の実験ではそこまでは区別できませんでした。 しかし、Direct3D12 で HDR が動いていることは確認できたので、今回はここまでとします。

詳しく説明するには、カラースペースやトーンマップやガンマや PBR の話もしないといけませんが、何が正しい表示なのか、結構難しい話になってしまうので、いつか書きたいと思います。 興味がある人は ChatGPT 先生にでも聞いてください。