1
\$\begingroup\$

In the sample code d3d12book-master\Chapter 6 Drawing in Direct3D\Box\BoxApp.cpp of "Introduction to 3D Game Programming with DirectX12 : Frank D. Luna", the camera position is modified via three variables mTheta for the azimuthal angle, mPhi for the polar angle, and mRadius, in the mouse move event handler.

As shown in the code, there is not any restriction to mTheta. However, mPhi is restricted to [0,PI]; if the restriction is commented out, the image changes suddenly at the boundaries of [0,PI]. Could you help to comment the reasoning behind such restriction ? Is it possible to see a continuous rotation of camera across the boundaries of [0,PI] ? O_O

initialized to certain values in world space in constructor

    XMFLOAT4X4 mWorld = MathHelper::Identity4x4();
    XMFLOAT4X4 mView = MathHelper::Identity4x4();
    XMFLOAT4X4 mProj = MathHelper::Identity4x4();

    float mTheta = 1.5f*XM_PI;
    float mPhi = XM_PIDIV4;
    float mRadius = 5.0f;

modified in mouse move

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y)
{
    if((btnState & MK_LBUTTON) != 0)
    {
        // Make each pixel correspond to a quarter of a degree.
        float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
        float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));

        // Update angles based on input to orbit camera around box.
        mTheta += dx;
        mPhi += dy;

        // Restrict the angle mPhi.
        mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);
    }

    mLastMousePos.x = x;
    mLastMousePos.y = y;
}

used to update camera postion

void BoxApp::Update(const GameTimer& gt)
{
    // Convert Spherical to Cartesian coordinates.
    float x = mRadius*sinf(mPhi)*cosf(mTheta);
    float z = mRadius*sinf(mPhi)*sinf(mTheta);
    float y = mRadius*cosf(mPhi);

    // Build the view matrix.
    XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
    XMVECTOR target = XMVectorZero();
    XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

    XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
    XMStoreFloat4x4(&mView, view);

    XMMATRIX world = XMLoadFloat4x4(&mWorld);
    XMMATRIX proj = XMLoadFloat4x4(&mProj);
    XMMATRIX worldViewProj = world*view*proj;

    // Update the constant buffer with the latest worldViewProj matrix.
    ObjectConstants objConstants;
    XMStoreFloat4x4(&objConstants.WorldViewProj, XMMatrixTranspose(worldViewProj));
    mObjectCB->CopyData(0, objConstants);
}
\$\endgroup\$
1
\$\begingroup\$

It's because of the Up vector.

It points up and allows your camera to be 'turned' correctly. And it's always the same: you don't change the value when you change he orientation of the camera.

From what I remember, functions such as XMMatrixLookAtLH will "fix" the up vector: it will first create the "side" vector from up and (target - eye), then from the "side" and (target - eye) it will recreate the "up" to make a correct transformation matrix.

This makes it all simple and "fixes itself" when you don't go over pi or 0, but when you do, it's as if you were telling the camera to 'flip' direction. And when you're exactly at zero or pi, you have your 'up' vector parallel to your 'forward' vector which is not enough to form a basis (you need two, which is what you basically provide to XMMatrixLookAtLH).

By clamping phi between [0, pi], you make sure that your transformation matrix is always valid, and it provides a 'smooth' experience to your user.


To fix this, I'm not exactly sure how you could only use phi and theta. I can't think of the right way from the top of my head because I don't remember having had to make such a camera. A way you could explore would be to keep a "side" vector that you update at the same time you update theta, and you use this side vector, along with the (target - eye) vector, to make the 'up' vector (by using the cross product).

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Many thanks for your helpful comments ! Nevertheless, for mPhi, is it possible to see a continuous rotation of camera across the boundaries of [0,PI] ? If yes, could you help to comment how to achieve the continuous effect ? O_O \$\endgroup\$ – SOUser Aug 7 '17 at 10:50
  • \$\begingroup\$ @SOUser See my edit. \$\endgroup\$ – Vaillancourt Aug 7 '17 at 11:06
  • 2
    \$\begingroup\$ If you want a camera that can look straight up or straight down, then controlling it with a pair of angles will not give you good results. (eg. imagine you're looking straight up and want to turn your head right – not pirouette around your nose, but turn your view to look over your right shoulder. Which angle do you change? theta does the pirouette thing, and phi will look up & down. You've lost your local yaw degree of freedom here) Using quaternions or basis vectors will let you handle the poles more uniformly. \$\endgroup\$ – DMGregory Aug 7 '17 at 12:17
  • \$\begingroup\$ @DMGregory Yeah, good point. \$\endgroup\$ – Vaillancourt Aug 7 '17 at 12:19
  • \$\begingroup\$ @DMGregory Thank you very much ! I will try to learn about your helpful comments :) \$\endgroup\$ – SOUser Aug 7 '17 at 12:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.