I have been spending a lot of time lately in developing 3D effects and some reusable controls for overlaying 3D effects on 2D controls (a subject of a forthcoming blog post). One of the core requirements is that the position of the PerspectiveCamera should be such that the 3D model is completely visible and has the same bounds as that of the Viewport3D. Now, what do I mean by that? A diagram should elucidate this concept.
What we really want!
Lets take the case where the model is positioned far in front of the camera, so we can see it completely. The camera position is (0, 0, 2.154)
However we want the model to be as big as the viewport and also completely visible, something like:
The camera position is (0, 0, 1.1525). This is how we want it positioned all the time. If we bring the camera forward a little more, we would be too close to the object, as in:
The camera position is (0, 0, 0.85).
What we want is that magic camera position that always seems to fit the model exactly as big as the viewport. Can we get that? Absolutely, but there is a little math involved in this.
Positioning the PerspectiveCamera
There are couple of quantities we need to fix to arrive at this camera position.
First of all we need the aspect ratio of the Viewport3D. This is simple: aspect = ActualWidth / ActualHeightof the Viewport3D instance.
Using this aspect ratio, we have to now define the mesh positions. Note that our mesh will always be positioned at Z=0, in the XY plane. Lets take a simple mesh of 4 points. These 4 points should be defined as:
mesh.Position = new Point3D(-aspect / 2, 0.5, 0); mesh.Position = new Point3D(aspect / 2, 0.5, 0); mesh.Position = new Point3D(aspect / 2, -0.5, 0); mesh.Position = new Point3D(-aspect / 2, -0.5, 0);
Finally its time for the camera position. Since we are using a PerspectiveCamera we need to take the FieldOfView property into account. Lets fix the FieldOfView = 60. With this the camera configuration looks like:
What we are looking at is the top view. The FieldOfView angle spans horizontally. The distance of the camera from the Origin (0, 0, 0), along the Z-axis is e. Using some simple trigonometry we can see that:
tan(30) = (aspect / 2) / e = aspect / 2e Thus e = aspect / 2 * tan(30) = aspect * 0.866 e = aspect * 0.86655
Lets take an example. Let the ActualWidth / ActualHeight of the Viewport3D be 400 / 300. So our aspect = 1.33. Also our FieldOfView = 60. We have also defined our mesh as:
<MeshGeometry3D Positions="-0.666,0.5,0 0.666,0.5,0 0.666,-0.5,0 -0.666,-0.5,0." Normals="0,0,1 0,0,1 0,0,1 0,0,1" TriangleIndices="0,3,2 0,2,1" TextureCoordinates="0,0 1,0 1,1 0,1"/\>
Therefore our camera Z-position should be at
e = 1.33 * 0.86655 = 1.1525. Thus the position is (0, 0, 1.1525). If you recollect this
is the same position from our discussion above (Figure 2). And ofcourse
we will have our PerspectiveCamera defined as:
<Viewport3D.Camera\> <PerspectiveCamera FieldOfView="60" Position="0,0,1.1525" LookDirection="0,0,-1" UpDirection="0,1,0"/\> </Viewport3D.Camera\>
In future posts I am going to build on this concept to create some reusable controls for overlaying 3D effects on 2D controls. Keep visiting!