OpenSiv3D
OpenSiv3D copied to clipboard
DebugCamera3D がコンストラクタで指定した注視点と異なる位置を注視するバグ
DebugCamera3D
がコンストラクタの引数 focusPosition
で指定した注視点と異なる位置を注視します。
下のスクリーンショットは BasicCamera3D
と DebugCamera3D
にそれぞれ赤い球を注目するようコンストラクタで指定したときの結果です。
BasicCamera3D
の場合は赤い球が中心にあるのに対し、 DebugCamera3D
の場合は赤い球が中心から外れています。
再現コード
# include <Siv3D.hpp>
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<String> 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 });
}
}
}
原因の考察と解決策
原因は 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));
}();