Direct3d

右手系と左手系の違い

Direct3Dでプログラムを書くといつも気になるのが左手系であること。 私は最初に3DCGを学んだのがOpenGLであったこともあり、右手系に慣れていたし、学生の頃に学んだ数学も右手系であったはず。 あと、ゲーム機のGLライクなAPIも右手系が多かったと思います。 なのに、Direct3Dが左手系を採用したことで、複雑な3DCGがさらに複雑になったと思います。

RenderDocを使ってDirect3D12アプリをデバッグ

シンプルなシェーダーでも正しく動いているのかデバッグするのは難しいです。 最初はPIXを使ってデバッグをしようと思い試してみたのですが、コンスタントバッファの中身は見えても、VertexShaderの頂点データが見えなくて、 設定が悪いのだろうかと思いつつ、ChatGPT先生に聞いてみると、困ったときはRenderDocを使え、という回答だったので、半信半疑でRenderDocを使うことにしました。

Blenderで作ったメッシュをDirect3D12で描画してみる

Direct3D12で任意のメッシュを描画してみようと思い、まず、メッシュデータを作りました。

仕事ではMayaを使っていますが、今回は、Blenderでデータを作ってみます。 初めてなので、Kindle Unlimitedでこちらの書籍を読みながらロケットのメッシュを作りました。

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);
}