Siv3D / OpenSiv3D

C++20 framework for creative coding 🎮🎨🎹 / Cross-platform support (Windows, macOS, Linux, and the Web)
https://siv3d.github.io/
MIT License
1.02k stars 140 forks source link

DebugCamera3D がコンストラクタで指定した注視点と異なる位置を注視するバグ #1255

Open Raclamusi opened 3 months ago

Raclamusi commented 3 months ago

DebugCamera3D がコンストラクタの引数 focusPosition で指定した注視点と異なる位置を注視します。

下のスクリーンショットは BasicCamera3DDebugCamera3D にそれぞれ赤い球を注目するようコンストラクタで指定したときの結果です。 BasicCamera3D の場合は赤い球が中心にあるのに対し、 DebugCamera3D の場合は赤い球が中心から外れています。

再現コード ```cpp # include void Main() { Window::Resize(1280, 720); const ColorF backgroundColor = ColorF{ 0.4, 0.6, 0.8 }.removeSRGBCurve(); const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB }; const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes }; // 同じパラメータを与えているが結果が異なる BasicCamera3D basicCamera{ renderTexture.size(), 30_deg, Vec3{ 10, 24, -32 }, Vec3{ 0, 2, 0 } }; DebugCamera3D debugCamera{ renderTexture.size(), 30_deg, Vec3{ 10, 24, -32 }, Vec3{ 0, 2, 0 } }; size_t index = 0; const Array options{ U"BasicCamera3D", U"DebugCamera3D" }; while (System::Update()) { if (index == 0) { Graphics3D::SetCameraTransform(basicCamera); } else { debugCamera.update(2.0); Graphics3D::SetCameraTransform(debugCamera); } { const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) }; Plane{ 64 }.draw(uvChecker); Sphere{ 0, 2, 0, 2 }.draw(ColorF{ 1.0, 0.0, 0.0 }.removeSRGBCurve()); } { Graphics3D::Flush(); renderTexture.resolve(); Shader::LinearToScreen(renderTexture); } { Line{ 630, 360, 650, 360 }.draw(2); Line{ 640, 350, 640, 370 }.draw(2); SimpleGUI::RadioButtons(index, options, Vec2{ 20, 20 }); } } } ```
BasicCamera3D の場合 DebugCamera3D の場合

原因の考察と解決策

原因は DebugCamera3D::m_focusY の初期化にあると考えられます。

https://github.com/Siv3D/OpenSiv3D/blob/9c77228c3f1642b1274f10be8a86379d37a0cfe1/Siv3D/include/Siv3D/DebugCamera3D.hpp#L35

DebugCamera3D の注視点は、カメラ位置を中心とする半径 1 の y 方向に無限に伸びる伸びる円筒上に正規化されます。 カメラ位置から注視点へのベクトルを正規化したベクトルを使って m_focusY を初期化していますが、このベクトルの y 成分が 0 でないとき、その指す位置は円筒上になく、その y 成分で m_focusY を初期化したとき本来指すべき位置より y 座標の絶対値が小さくなります。

つまり、カメラ位置から注視点へのベクトルを円筒上に来るように正規化し、 m_focusY を初期化することでこの問題は解決できます。 例えば、 m_focusY の初期化を次のように変更します。

double m_focusY = [this]
    {
        const auto focusVector = (BasicCamera3D::m_focusPosition - BasicCamera3D::m_eyePosition);
        return (focusVector.y / std::hypot(focusVector.x, focusVector.z));
    }();
Raclamusi commented 2 weeks ago

DebugCamera3D::setView() にも同様のバグがありました。

Reputeless commented 2 weeks ago

コメントありがとうございます! なぜかこの Issue の存在が記憶から抜け落ちてました(多分忙しかった時期だったため)。 対応が必要なので、提案を試してみます。よさそうだと判断した場合 PR 受け付けます。

Reputeless commented 1 week ago

問題を確認しました。修正方法も妥当だと思います。PR 受け付けます!