右手系と左手系の違い
Direct3Dでプログラムを書くといつも気になるのが左手系であること。 私は最初に3DCGを学んだのがOpenGLであったこともあり、右手系に慣れていたし、学生の頃に学んだ数学も右手系であったはず。 あと、ゲーム機のGLライクなAPIも右手系が多かったと思います。 なのに、Direct3Dが左手系を採用したことで、複雑な3DCGがさらに複雑になったと思います。
Direct3Dでプログラムを書くといつも気になるのが左手系であること。 私は最初に3DCGを学んだのがOpenGLであったこともあり、右手系に慣れていたし、学生の頃に学んだ数学も右手系であったはず。 あと、ゲーム機のGLライクなAPIも右手系が多かったと思います。 なのに、Direct3Dが左手系を採用したことで、複雑な3DCGがさらに複雑になったと思います。
シンプルなシェーダーでも正しく動いているのかデバッグするのは難しいです。 最初はPIXを使ってデバッグをしようと思い試してみたのですが、コンスタントバッファの中身は見えても、VertexShaderの頂点データが見えなくて、 設定が悪いのだろうかと思いつつ、ChatGPT先生に聞いてみると、困ったときはRenderDocを使え、という回答だったので、半信半疑でRenderDocを使うことにしました。
Direct3D12で任意のメッシュを描画してみようと思い、まず、メッシュデータを作りました。
仕事ではMayaを使っていますが、今回は、Blenderでデータを作ってみます。 初めてなので、Kindle Unlimitedでこちらの書籍を読みながらロケットのメッシュを作りました。
前回、HDRのディスプレイで、R10G10B10A2のフォーマットを描画することを試して、 とりあえず、R8G8B8A8よりも明るく表示されることは確認しました。
では、どのように調整することで、SDRとHDRの見た目を近づけることができるのか、試したいと思います。
前回に続いて、ディスプレイ関連です。
前回も書きましたが、最近買ったこのディスプレイは、HDR600に対応しています。
Dell AW2724DM 27インチ Alienware ゲーミングモニター
HDR600の説明は以下の記事などが分かりやすいです。
前回までに、フルスクリーンモードで自由にリフレッシュレートを変更できるようになりましたので、 今回は、可変リフレッシュレートの動作を確認したいと思います。
私が使っているディスプレイは2台ありまして、1台は最近買ったこのディスプレイです。
前回までに、リフレッシュレートを自由に設定できるようになりましたので、 今回は描画のfpsを自由に設定できるようにします。
そのためには、ダブルバッファをトリプルバッファに変更して、VSync待ちをなくして、Presentを実行する必要があります。
Direct3Dに限った話ではありませんが、PCのアプリは、フレームレートを制御する必要があります。 ゲーム機のように、固定されたリフレッシュレートに合わせてフレームレートを決めることができる場合は、 VSyncを待つだけでフレームレートを制御できますが、PCのモニターはリフレッシュレートが様々なので、アプリ自身がフレームレートを制御する必要がでてきます。
ウィンドウモードの場合は解像度はウィンドウサイズを変更することで設定できます。(もちろん、RTVは作り直します。) しかし、ウィンドウ単位でリフレッシュレートは設定できません。
リフレッシュレートを変更するためには、フルスクリーンで動いている必要があります。 前回の記事で、フルスクリーンへの切り替え方法を書きましたので、今回はその続きになります。
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);
}
1/2
»