Automatically set camera distance in WPF and C#

[camera distance]

This example extends the post Build a geodesic sphere with WPF and C# to let you automatically set the camera distance to make the scene fill the viewport.

Making a 3D scene fill the viewport nicely can be tricky. The basic idea is to position the camera the right distance from the scene to give a good result. However, some scenes are asymmetric so the “best” distance will depend on the angle from which you are viewing the scene.

One approach that often gives fairly good results is to use the camera’s field of view and the radius of the scene’s bounding sphere to set the camera distance. To do that, you need to know how the field of view works.

Field of View

[camera distance]

The picture on the right shows how the camera’s field of view affects the drawing. The field of view is the (horizontal) angle that the camera can see. Here width is the width of the viewport and distance is the distance between the camera and the virtual viewing plane. For our purposes, distance is the camera distance, or the distance between the camera and the center of the scene’s objects.

Simple trigonometry gives us:

[camera distance]
If we solve for distance, we get:

[camera distance]
In order to make the scene’s objects fill the viewport, we set the distance so the width/2 is the radius of the scene’s bounding sphere.

C# Code

When you click the program’s Set Size button, the following code executes.

private void btnSetSize_Click(object sender, RoutedEventArgs e)
    const double width = 2.5;
    double fov_radians = TheCamera.FieldOfView * Math.PI / 180.0;
    double distance = (width / 2) / Math.Tan(fov_radians / 2);

    const double distance_scale = 1.0;
    CameraR = distance * distance_scale;
    lblDistance.Content = CameraR.ToString("0.0");

This example’s scene is a geodesic sphere with some axes. The axes extend to points 1.25 units away from the origin, so I’m giving the bounding sphere for this example a radius of 1.25 or a diameter of 2.5. That means the desired width value should be 2.5.

The camera’s FieldOfView property holds the field of view in degrees but the Math.Tan function needs its input angle in radians, so the program converts the field of view value into radians.

Next the code uses the formula shown earlier to calculate the desired camera distance. It then sets the camera distance to that value multiplied by a scale factor. You can use the scale factor if you want to add or subtract a little distance to make your scene look better. This example looks okay without scaling, so distance_scale is set to 1.0.

The code finishes by displaying the new distance and repositioning the camera to show the new result.


[WPF 3d]

Automatically setting camera distance can be tricky depending on the objects in your scene. This example calculates a camera distance from the camera’s field of view and a bounding sphere diameter (width). You can add a scale factor to adjust the result if that makes sense for your scene.

For more information on creating three-dimensional scenes with WPF and C#, see my book WPF 3d.

Meanwhile, download the example to see additional details and to experiment with it.

Download Example   Follow me on Twitter   RSS feed   Donate

About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in 3D graphics, algorithms, graphics, mathematics, wpf and tagged , , , , , , , , , , , , , , , , . Bookmark the permalink.

6 Responses to Automatically set camera distance in WPF and C#

  1. Osama says:

    Hello Rod,

    Thank’s alot for your help! But I’m still sticking on this matter. You have always a symmetrical geometry, but I not.
    Nevertheless, you have to define manually the parameter CameraPhi and CameraTheta, because you are still using the function PositionCamera().

    I brought your book and find it fascinating, but I do not wanna read it from the beginning, because I do not have enough time for that!

    Could you tell me, if there is some examples in your book, which are related to my issue?

    Best regards

    • RodStephens says:

      I don’t think there’s anything in the book that covers this. This post is probably your best starting point.

      You should pick a center point for the scene and point the camera at that. Then you can define a radius for a sphere that defines the area of interest and use this post’s techniques to fit to that.

      The interest sphere could include every object, in which case the scene might have some empty areas around the edges when seen from certain directions. You could also make the interest sphere include only the objects in the center that are most important, in which case some objects may be cut off the edges of the scene when viewed from certain directions.

      Another approach would be to do the math to project the scene for the given viewing direction and then decide how far away you need to be to make it fit nicely for that viewing angle. I don’t think the book covers that and I don’t have time to give it a tray right now.

      I’m also not sure there is a perfect way to do this. Sometimes you may want to crop tightly to see the center of the scene, but other times you may want to see everything. This is one of the reasons why I like to give the user control so they can adjust as needed.

  2. Osama says:

    Hello Rod,

    the issue is solved, but i have now another problem. Did you use canvas somewhere in your book. In my Window, there is also buttons. I do not want, that my geometry shown under a button.

    Best regards

    • RodStephens says:

      I’m not sure what you want. Do you want buttons to live inside the 3D scene?

      Normally I put buttons and other controls outside of the 3D viewport so the controls are separate. You can detect clicks on the objects inside the scene, however, so you could make a 3D box or something with labels on the sides and use that for a button.

Comments are closed.