How to: Position the Camera to View All Objects in a Scene

This example demonstrates how to position the camera so that all objects in a scene are within the view frustum while maintaining the camera's original orientation.

To position the camera to view all objects in a scene

  1. Create a BoundingSphere class that contains all of the objects in the scene. To create the sphere, loop through all of the objects in the scene, merging the BoundingSphere classes that contain them with BoundingSphere.CreateMerged. If you are not already tracking the BoundingSphere classes for collision detection, use CreateFromBoundingBox or CreateFromPoints to create them from BoundingBox classes or points. In this example, the BoundingSphere classes are created from BoundingBox classes.

    BoundingSphere GetSceneSphere()
    {
        BoundingSphere sceneSphere = new BoundingSphere( new Vector3( .5f, 1, .5f ), 1.5f );
        for (int z = 0; z < 5; z++)
        {
            for (int x = 0; x < 5; x++)
            {
                BoundingSphere boundingSphere = sphere.Meshes[0].BoundingSphere;
                boundingSphere.Center = new Vector3( x * 3, 0, z * 3 );
    
                sceneSphere = BoundingSphere.CreateMerged( sceneSphere, boundingSphere );
            }
        }
    
        return sceneSphere;
    }
    
  2. Set the position of the camera to the center of the BoundingSphere that contains the scene.

    cameraPosition = sceneSphere.Center;
    
  3. Determine the distance from the center of the BoundingSphere that the camera needs to be to view the entire scene. This distance is equal to the hypotenuse of the triangle formed by the center of the sphere, the desired camera position, and the point where the sphere touches the view frustum. One angle of the triangle is known to be the field of view of the camera divided by two. One leg of the triangle is known to be the radius of the sphere. Given these two measurements, you can calculate the hypotenuse as the radius of the sphere divided by the sine of half the field of view.

    float distanceToCenter = sceneSphere.Radius / (float)Math.Sin( FOV / 2 );
    
  4. Get the Backward vector of the view Matrix and flip its X component.

    Vector3 back = view.Backward;
    back.X = -back.X; //flip x's sign
    
  5. To move the camera backward with respect to its orientation, multiply the desired distance by the adjusted back vector from the previous step. The camera is now facing the center of the sphere containing the scene and is far enough back that the sphere fits in the camera's view frustum.

    cameraPosition += (back * distanceToCenter);
    
//Game1.cs
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

class Game1 : Microsoft.Xna.Framework.Game
{
    Matrix view;
    Matrix proj;

    Model sphere;
    Texture2D sphereTexture;

    ContentManager contentManager;
    GraphicsDeviceManager graphics;

    // Set the avatar position and rotation variables.
    Vector3 cameraPosition = new Vector3( 0, 0, -8 );

    float cameraYaw;

    // Setthe direction the camera points without rotation.
    Vector3 cameraReference = new Vector3( 0, 0, 10 );



    // Set rates in world units per 1/60th second (the default fixed step interval)
    float rotationSpeed = 1f / 60f;
    float forwardSpeed = 25f / 60f;

    // Set information about the game window.
    static int screenWidth = 800;
    static int screenHeight = 600;
    static float aspectRatio = (float)screenWidth / (float)screenHeight;

    // Set feld of view of the camera in radians (pi/4 is 45 degrees).
    static float FOV = MathHelper.PiOver4;

    // Z values of the near and far clipping planes.
    static float nearClip = 5.0f;
    static float farClip = 1000.0f;

    // Update the position and direction of the avatar.
    void UpdateAvatarPosition()
    {
        KeyboardState keyboardState = Keyboard.GetState();
        GamePadState currentState = GamePad.GetState( PlayerIndex.One );

        if (keyboardState.IsKeyDown( Keys.Left ) || (ButtonState.Pressed == currentState.DPad.Left))
        {
            // Rotate left.
            cameraYaw += rotationSpeed;
        }
        if (keyboardState.IsKeyDown( Keys.Right ) || (ButtonState.Pressed == currentState.DPad.Right))
        {
            // Rotate right.
            cameraYaw -= rotationSpeed;
        }
        if (keyboardState.IsKeyDown( Keys.Up ) || (ButtonState.Pressed == currentState.DPad.Up))
        {
            Matrix forwardMovement = Matrix.CreateRotationY( cameraYaw );
            Vector3 v = new Vector3( 0, 0, forwardSpeed );
            v = Vector3.Transform( v, forwardMovement );
            cameraPosition.Z += v.Z;
            cameraPosition.X += v.X;
        }
        if (keyboardState.IsKeyDown( Keys.Down ) || (ButtonState.Pressed == currentState.DPad.Down))
        {
            Matrix forwardMovement = Matrix.CreateRotationY( cameraYaw );
            Vector3 v = new Vector3( 0, 0, -forwardSpeed );
            v = Vector3.Transform( v, forwardMovement );
            cameraPosition.Z += v.Z;
            cameraPosition.X += v.X;
        }
    }

    void GetCurrentCamera()
    {
        KeyboardState keyboardState = Keyboard.GetState();
        GamePadState currentState = GamePad.GetState( PlayerIndex.One );

        if (keyboardState.IsKeyDown( Keys.C ) || (currentState.Buttons.LeftShoulder == ButtonState.Pressed))
        {
            FitCameraToScene();
        }
    }

    protected override void Update( GameTime gameTime )
    {
        base.Update( gameTime );
        GetCurrentCamera();
        UpdateAvatarPosition();
    }

    protected override void Draw( GameTime gameTime )
    {
        base.Draw( gameTime );
        FrameRender();
    }

    void UpdateCamera()
    {
        Matrix rotationMatrix = Matrix.CreateRotationY( cameraYaw );

        // Create a vector pointing the direction the camera is facing.
        Vector3 transformedReference = Vector3.Transform( cameraReference, rotationMatrix );

        // Calculate the position the camera is looking at.
        Vector3 cameraLookat = cameraPosition + transformedReference;

        // Set up the view matrix and projection matrix.
        view = Matrix.CreateLookAt( cameraPosition, cameraLookat, new Vector3( 0.0f, 1.0f, 0.0f ) );

        proj = Matrix.CreatePerspectiveFieldOfView( FOV, aspectRatio, nearClip, farClip );
    }

    void FrameRender()
    {
        GraphicsDevice device = graphics.GraphicsDevice;

        device.Clear( ClearOptions.Target | ClearOptions.DepthBuffer, new Vector4( 0, 0, 0, 255 ), 1.0f, 0 );

        UpdateCamera();


        for (int z = 0; z < 5; z++)
        {
            for (int x = 0; x < 5; x++)
            {
                DrawModel( sphere, Matrix.CreateTranslation( new Vector3( x * 3, 0, z * 3 ) ), sphereTexture );
            }
        }

    }

    public Game1()
    {
        graphics = new GraphicsDeviceManager( this );
        contentManager = new ContentManager( Services );
    }

    protected override void LoadGraphicsContent( bool loadAllContent )
    {
        base.LoadGraphicsContent( loadAllContent );
        if (loadAllContent)
        {
            sphere = contentManager.Load<Model>( "sphere" );
            sphereTexture = contentManager.Load<Texture2D>( "spheretexture" );
        }
    }

    protected override void UnloadGraphicsContent( bool unloadAllContent )
    {
        base.UnloadGraphicsContent( unloadAllContent );
        if (unloadAllContent)
        {
            contentManager.Unload();
        }
    }


    BoundingSphere GetSceneSphere()
    {
        BoundingSphere sceneSphere = new BoundingSphere( new Vector3( .5f, 1, .5f ), 1.5f );
        for (int z = 0; z < 5; z++)
        {
            for (int x = 0; x < 5; x++)
            {
                BoundingSphere boundingSphere = sphere.Meshes[0].BoundingSphere;
                boundingSphere.Center = new Vector3( x * 3, 0, z * 3 );

                sceneSphere = BoundingSphere.CreateMerged( sceneSphere, boundingSphere );
            }
        }

        return sceneSphere;
    }

    void FitCameraToScene()
    {
        BoundingSphere sceneSphere = GetSceneSphere();

        cameraPosition = sceneSphere.Center;

        float distanceToCenter = sceneSphere.Radius / (float)Math.Sin( FOV / 2 );


        Vector3 back = view.Backward;
        back.X = -back.X; //flip x's sign

        cameraPosition += (back * distanceToCenter);

    }

    void DrawModel( Model model, Matrix world, Texture2D texture )
    {
        foreach (ModelMesh mesh in model.Meshes)
        {
            foreach (BasicEffect be in mesh.Effects)
            {
                be.Projection = proj;
                be.View = view;
                be.World = world;
                be.Texture = texture;
                be.TextureEnabled = true;
            }
            mesh.Draw();
        }
    }
}

See Also

How to: Rotate and Move a Camera
How to: Make a First-Person Camera
How to: Make a Third-Person Camera
How to: Render a Model