2024/10

SDRとHDRで見た目を同じにするには

前回、HDRのディスプレイで、R10G10B10A2のフォーマットを描画することを試して、 とりあえず、R8G8B8A8よりも明るく表示されることは確認しました。

では、どのように調整することで、SDRとHDRの見た目を近づけることができるのか、試したいと思います。

Direct3D12でG-SYNC Compatibleを試す

前回までに、フルスクリーンモードで自由にリフレッシュレートを変更できるようになりましたので、 今回は、可変リフレッシュレートの動作を確認したいと思います。

私が使っているディスプレイは2台ありまして、1台は最近買ったこのディスプレイです。

フレームレートを制御する

Direct3Dに限った話ではありませんが、PCのアプリは、フレームレートを制御する必要があります。 ゲーム機のように、固定されたリフレッシュレートに合わせてフレームレートを決めることができる場合は、 VSyncを待つだけでフレームレートを制御できますが、PCのモニターはリフレッシュレートが様々なので、アプリ自身がフレームレートを制御する必要がでてきます。

Direct3D12で解像度とリフレッシュレートを設定する

ウィンドウモードの場合は解像度はウィンドウサイズを変更することで設定できます。(もちろん、RTVは作り直します。) しかし、ウィンドウ単位でリフレッシュレートは設定できません。

リフレッシュレートを変更するためには、フルスクリーンで動いている必要があります。 前回の記事で、フルスクリーンへの切り替え方法を書きましたので、今回はその続きになります。

Direct3D12でフルスクリーンに切り替える

ChatGPT先生に聞きながら、Direct3D12でフルスクリーンモードとウィンドウモードの切り替えを実装しました。 F11キーを押すことで切り替えるというものです。

SampleApp g_app;

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    // ImGuiのウィンドウ処理
    if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam)) {
        return true;
    }

    static bool isFullscreen = false;
    static bool isFullscreenInActive = false;
    static WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) }; // ウィンドウモード時の位置とサイズを保存

    switch (message) {
    case WM_DESTROY:
        g_app.SetFullscreenState(FALSE);
        PostQuitMessage(0);
        return 0;
    case WM_KEYDOWN:
        if (wParam == VK_F11) { // F11キーで切り替え
            if (isFullscreen) {
                // ウィンドウモードに戻す
                g_app.SetFullscreenState(FALSE);
                SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                SetWindowPlacement(hWnd, &windowPlacement); // 以前のウィンドウの位置とサイズを復元
                SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
                isFullscreen = false;
            }
            else {
                // フルスクリーンモードにする
                GetWindowPlacement(hWnd, &windowPlacement); // 現在のウィンドウの位置とサイズを保存
                SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE); // ウィンドウの境界線とタイトルバーを削除
                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
                //SetWindowPos(hWnd, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);
                g_app.SetFullscreenState(TRUE);
                isFullscreen = true;
            }
        }
        return 0;
    case WM_SIZE:
        if (wParam != SIZE_MINIMIZED) {
            UINT width = LOWORD(lParam);
            UINT height = HIWORD(lParam);

            g_app.SetScreenSize(width, height);
        }
        break;
    case WM_ACTIVATE:
        if (wParam == WA_INACTIVE) {
            // ウィンドウが非アクティブになった場合
            if (isFullscreen) {
                // フルスクリーンモードからウィンドウモードに戻す
                g_app.SetFullscreenState(FALSE);
                SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                SetWindowPlacement(hWnd, &windowPlacement); // 以前の位置とサイズを復元
                SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
                isFullscreenInActive = true;
            }
        }
        else if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) {
            // ウィンドウが再度アクティブになった場合
            if (isFullscreenInActive) {
                // 必要ならフルスクリーンに戻す
                SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
                g_app.SetFullscreenState(TRUE);
                isFullscreenInActive = false;
            }
        }
        return 0;
    default:
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

	

Direct3D12でimguiを使ってみる

Direct3Dのアプリを書くには、デバッグ描画が必要です。デバッグ描画とは、例えば、画面上にフレームレートや様々な設定を描画するものです。 最終的な商品では使われないものなので、切り離してコンパイルできるように作りたいです。

Direct3D12アプリを書いてみる(3)

Direct3D12は、描画コマンドをコマンドリストにバッファリングして、まとめて実行するという形式となります。 毎フレーム実行する処理を簡易的に書くと以下のようになります。

{
    // コマンドリストの開始
    commandAllocator->Reset();
    commandList->Reset(commandAllocator.Get(), pipelineState.Get());

    // 様々な描画コマンドを実行して、コマンドリストを作成
    ...

    // バッファリングされたコマンドリストを実行
    commandList->Close();
    ID3D12CommandList* commandLists[] = { commandList.Get() };
    commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);

    // スワップチェーンの表示
    swapChain->Present(1, 0);
}

	

Direct3D12アプリを書いてみる(2)

まず、Direct3D12のプログラムを説明する前に、Windowだけを表示するプログラムを書きます。 基本的には、CreateWindowでWindowを生成し、メインループでメッセージを処理するだけです。