Setting Up a View Matrix

The D3DUtil_SetViewMatrix helper function, from the D3dutil.cpp source file that is included with this SDK, creates a view matrix based on the camera location and a look-at point passed to it. It uses the Magnitude, CrossProduct, and DotProduct D3D_OVERLOADS helper functions.

HRESULT D3DUtil_SetViewMatrix( D3DMATRIX& mat, D3DVECTOR& vFrom, 
                               D3DVECTOR& vAt, D3DVECTOR& vWorldUp )
{
    // Get the z basis vector, which points straight ahead. This is the
    // difference from the eyepoint to the lookat point.
    D3DVECTOR vView = vAt - vFrom;
 
    FLOAT fLength = Magnitude( vView );
    if( fLength < 1e-6f )
        return E_INVALIDARG;
 
    // Normalize the z basis vector
    vView /= fLength;
 
    // Get the dot product, and calculate the projection of the z basis
    // vector onto the up vector. The projection is the y basis vector.
    FLOAT fDotProduct = DotProduct( vWorldUp, vView );
 
    D3DVECTOR vUp = vWorldUp - fDotProduct * vView;
 
    // If this vector has near-zero length because the input specified a
    // bogus up vector, let's try a default up vector
    if( 1e-6f > ( fLength = Magnitude( vUp ) ) )
    {
        vUp = D3DVECTOR( 0.0f, 1.0f, 0.0f ) - vView.y * vView;
 
        // If we still have near-zero length, resort to a different axis.
        if( 1e-6f > ( fLength = Magnitude( vUp ) ) )
        {
            vUp = D3DVECTOR( 0.0f, 0.0f, 1.0f ) - vView.z * vView;
 
            if( 1e-6f > ( fLength = Magnitude( vUp ) ) )
                return E_INVALIDARG;
        }
    }
 
    // Normalize the y basis vector
    vUp /= fLength;
 
    // The x basis vector is found simply with the cross product of the y
    // and z basis vectors
    D3DVECTOR vRight = CrossProduct( vUp, vView );
    
    // Start building the matrix. The first three rows contains the basis
    // vectors used to rotate the view to point at the lookat point
    D3DUtil_SetIdentityMatrix( mat );
    mat._11 = vRight.x;    mat._12 = vUp.x;    mat._13 = vView.x;
    mat._21 = vRight.y;    mat._22 = vUp.y;    mat._23 = vView.y;
    mat._31 = vRight.z;    mat._32 = vUp.z;    mat._33 = vView.z;
 
    // Do the translation values (rotations are still about the eyepoint)
    mat._41 = - DotProduct( vFrom, vRight );
    mat._42 = - DotProduct( vFrom, vUp );
    mat._43 = - DotProduct( vFrom, vView );
 
    return S_OK;
}
 

As with the world transformation, you call the IDirect3DDevice3::SetTransform method to set the view transformation, specifying the D3DTRANSFORMSTATE_VIEW flag in the first parameter. See Setting Transformations, for more information.

Performance Optimization  Direct3D uses the world and view matrices that you set through IDirect3DDevice3::SetTransform to configure several of its internal data structures. Each time you set a new world or view matrix, the system recalculates the associated internal structures. Setting these matrices frequently—for example, 20000 times per frame—is computationally expensive. You can minimize the number of required calculations by concatenating your world and view matrices into a proverbial "world-view" matrix that you set as the world matrix, then set the view matrix to the identity. Keep cached copies of individual world and view matrices that you can modify, concatenate, and reset the world matrix as needed. (For clarity, Direct3D samples rarely employ this optimization.)