Cameras in 3D

The MuPAD® 3D graphics model includes an observer at a specific position, pointing a camera with a lens of a specific opening angle to some specific focal point. The specific parameters "position", "angle", and "focal point" determine the picture that the camera will take.

When a 3D picture is created in MuPAD, a camera with an appropriate default lens is positioned automatically. Its focal point is chosen as the center of the graphical scene. The interactive viewer allows to rotate the scene which, in fact, is implemented internally as a change of the camera position. Also interactive zooming in and zooming out is realized by moving the camera closer to or farther away from the scene.

Apart from interactive camera motions, the perspective of a 3D picture can also be set in the calls generating the plot. One way is to specify the direction from which the camera is pointing towards the scene. This is done via the attribute CameraDirection:

plot(plot::Function3d(sin(x + y^3), x = -1..1, y = -1..1),
     CameraDirection = [-25, 20, 30]):

plot(plot::Function3d(sin(x + y^3), x = -1..1, y = -1..1),
     CameraDirection = [10, -40, 10]):

In these calls, the position of the camera is not fully specified by CameraDirection. This attribute just requests the camera to be placed at some large distance from the scene along the ray in the direction given by the attribute. The actual distance from the scene is determined automatically to let the graphical scene fill the picture optimally.

For a full specification of the perspective, there are camera objects of type plot::Camera that allow to specify the position of the camera, its focal point and the opening angle of its lens:

position := [-5, -10, 5]:
focalpoint := [0, 0, 0]:
angle := PI/12:
camera := plot::Camera(position, focalpoint, angle):

This camera can be passed like any graphical object to the plot command generating the scene. Once a camera object is specified in a graphical scene, it determines the view. No "automatic camera" is used:

plot(plot::Function3d(sin(x + y^3), x = -1..1, y = -1..1),
     camera):

Camera objects can be animated:

camera := plot::Camera([3*cos(a), 3*sin(a), 1 + cos(2*a)],
                       [0, 0, 0], PI/3, a = 0..2*PI, 
                       Frames = 100):

Inserting the animated camera in a graphical scene, we obtain an animated plot simulating a "flight around the scene":

plot(plot::Function3d(sin(x + y^3), x = -1..1, y = -1..1),
     camera):

In fact, several cameras can be installed simultaneously in a scene:

camera1 := plot::Camera([3*cos(a), 3*sin(a), 1 + cos(2*a)],
                        [0, 0, 0], PI/3, a = 0..2*PI,
                        Name = "Camera 1"):
camera2 := plot::Camera([2*cos(a), 2*sin(a), 2 + cos(2*a)],
                        [0, 0, 0], PI/3, a = 0..2*PI,
                        Name = "Camera 2"):
plot(plot::Function3d(sin(x + y^3), x = -1..1, y = -1..1),
     camera1, camera2):

Per default, the first camera produces the view rendered. After clicking on another camera in the object browser of the viewer (see section Viewer, Browser, and Inspector: Interactive Manipulation), the selected camera takes over and the new view is shown.

Next, we have a look at a more appealing example: the so-called "Lorenz attractor." The Lorenz ODE is the system

with fixed parameters p, r, b. As a dynamic system for Y = [x, y, z], we have to solve the ODE with the following vector field:

f := proc(t, Y)
     local x, y, z;
     begin
        [x, y, z] := Y:
        [p*(y - x), -x*z + r*x - y, x*y - b*z]
     end_proc:

Consider the following parameters and the following initial condition Y0:

p := 10: r := 28: b := 1: Y0 := [1, 1, 1]:

The routine plot::Ode3d serves for generating a graphical 3D solution of a dynamic system. It solves the ODE numerically and generates graphical data from the numerical mesh. The plot data are specified by the user via "generators" (procedures) that map a solution point (t, Y) to a point (x, y, z) in 3D.

The following generator Gxyz produces a 3D phase plot of the solution. The generator Gyz projects the solution curve to the (y, z) plane with x = - 20; the generator Gxz projects the solution curve to the (x, z) plane with y = - 20; the generator Gxy projects the solution curve to the (x, y) plane with z = 0:

Gxyz := (t, Y) -> Y:
Gyz := (t, Y) -> [-20, Y[2], Y[3]]:
Gxz := (t, Y) -> [Y[1], -20, Y[3]]:
Gxy := (t, Y) -> [Y[1], Y[2], 0]:

With these generators, we create a 3D plot object consisting of the phase curve and its projections. The following command calls the numerical solver numeric::odesolve to produce the graphical data. It takes about half a minute on a 1 GHz computer:

object := plot::Ode3d(f, [i/10 $ i=1..500], Y0,
           [Gxyz, Style = Splines, Color = RGB::Red],
           [Gyz, Style = Splines, Color = RGB::LightGrey],
           [Gxz, Style = Splines, Color = RGB::LightGrey],
           [Gxy, Style = Splines, Color = RGB::LightGrey]):

We define an animated camera moving around the scene:

camera := plot::Camera([-1 + 100*cos(a), 6 + 100*sin(a), 120],
                       [-1, 6, 25], PI/6, a = 0..2*PI, 
                       Frames = 120):

The following plot call also takes about half a minute on a 1 GHz computer:

plot(object, camera, Axes = Boxed, TicksNumber = Low):

Next, we wish to fly along the Lorenz attractor. We cannot use plot::Ode3d, because we need access to the numerical data of the attractor to build a suitable animated camera object. We use the numerical ODE solver numeric::odesolve2 and compute a list of numerical sample points on the Lorenz attractor. This takes about half a minute on a 1 GHz computer:

Y := numeric::odesolve2(f, 0, Y0, RememberLast):
timemesh :=  [i/50 $ i = 0..2000]:   
Y := [Y(t) $ t in timemesh]:

Similar to the picture above, we define a box around the attractor with the projections of the solution curve:

box := [-15, 20, -20, 26, 1, 50]:
Yyz := map(Y, pt -> [box[1], pt[2], pt[3]]):
Yxy := map(Y, pt -> [pt[1], pt[2], box[5]]):
Yxz := map(Y, pt -> [pt[1], box[3], pt[3]]):

We create an animated camera using an animation parameter a that corresponds to the index of the list of numerical sample points. The following procedure returns the i-th coordinate (i = 1, 2, 3) of the a-th point in the list of sample points:

Point := proc(a, i)
begin
  if domtype(float(a)) <> DOM_FLOAT then
       procname(args());
  else Y[round(a)][i];
  end_if;
end_proc:

In the a-th frame of the animation, the camera is positioned at the a-th sample point of the Lorenz attractor, pointing toward the next sample point. Setting TimeRange = 0..n/10, the camera visits about 10 points per second:

n := nops(timemesh) - 1:
plot(plot::Scene3d(
   plot::Camera([Point(a, i)    $ i = 1..3], 
                [Point(a + 1, i) $ i = 1..3],
                 PI/4, a = 1..n, Frames = n, 
                 TimeRange = 0..n/10),
   plot::Polygon3d(Y, LineColor = RGB::Red, 
                   PointsVisible = TRUE),
   plot::Polygon3d(Yxy, LineColor = RGB::DimGrey),
   plot::Polygon3d(Yxz, LineColor = RGB::DimGrey),
   plot::Polygon3d(Yyz, LineColor = RGB::DimGrey),
   plot::Box(box[1]..box[2], box[3]..box[4], box[5]..box[6],
             LineColor = RGB::Black, Filled = TRUE,
             FillColor = RGB::Grey.[0.1]),
   BackgroundStyle = Flat)
):

Was this topic helpful?