Documentation



JavaFX: Working with JavaFX Graphics

3 Camera

This chapter describes the Camera API that is included with the JavaFX 3D Graphics features.

The camera is now a node that can be added to the JavaFX scene graph and thus allows you to move the camera around in a 3D UI layout. This is different from the 2D layout where the camera remained in one position.

In the JavaFX scene coordinate space, the default camera's projection plane is at Z=0 and camera coordinate system is as follows:

• X-axis points to the right

• Y-axis points down

• Z-axis points away from the viewer or into the screen.

Perspective Camera

JavaFX provides a perspective camera for rendering a 3D scene. This camera defines a viewing volume for a perspective projection. The viewing volume can be changed by changing the value of the fieldOfView property.

Example 3-1 shows the two constructors for creating a Perspective Camera:

Example 3-1 Constructors for PerspectiveCamera

PerspectiveCamera()

PerspectiveCamera(boolean fixedEyeAtCameraZero)

The latter constructor is a new constructor in JavaFX 8 and allows you to control the camera position with the specified fixedEyeAtCameraZero flag so that it renders what the camera would see in a 3D environment.

The following constructor should be used for 3D graphics programming:

PerspectiveCamera(true);

When the option fixedEyeAtCameraZero is set to true, a PerspectiveCamera is constructed with its eye position fixed at (0, 0, 0) in its coordinate space, regardless of the change in the dimension of the projection area or window resize.

When the fixedEyeAtCameraZero is set to the default value of false, the coordinate system defined by the camera has its origin in the upper left corner of the panel. This mode is used for 2D UI controls rendered with a perspective camera, but is not useful for most 3D graphics applications. The camera is moved when the window is resized, for example, to maintain the origin in the upper left corner of the panel. That is exactly what you want for a 2D UI layout, but not in a 3D layout. So, it is important to remember to set the fixedEyeAtCameraZero property to true when you are doing 3D graphics to transform or move the camera around.

To create a camera and add it to the scene, use the following lines of code:

Camera camera = new PerspectiveCamera(true);
     scene.setCamera(camera);

Use the following code to add a camera to the scene graph.

Group cameraGroup = new Group();
     cameraGroup.getChildren().add(camera);
     root.getChildren().add(cameraGroup);

To rotate the camera and move the cameraGroup, use the following lines of code:

camera.rotate(45);
     cameraGroup.setTranslateZ(-75);

Field of View

The camera's field of view can be set as follows:

camera.setFieldOfView(double value);

The larger the field of view, the more perspective distortion and size differences increase.

  • Fisheye lenses have a field of view of up to 180 degrees and beyond.

  • Normal lenses have a field of view between 40 and 62 degrees.

  • Telephoto lenses have a field of view of 1 (or less) degrees to 30 degrees.

Clipping Planes

You can set the near clipping plane of the Camera in the local coordinate system as follows:

camera.setNearClip(double value);

To set the far clipping plane of the Camera in the local coordinate system, use the following:

camera.setFarClip(double value);

Setting the near or far clipping planes determines the viewing volume. If the near clipping plane is too great, it will basically start clipping the front of the scene. If it is too small, then it will start clipping the back of the scene.

Tip:

Don't set the near clipping value to a smaller value than is needed or the far clipping value to a larger value than is needed because strange visual artifacts may start appearing.

The clipping planes need to be set so that enough of the scene can be seen. But the viewing range should not be set so large that a numerical error is encountered. If the near clipping plane is too large a value, the scene starts getting clipped. But if the near clipping plane too small, a different kind of visual artifact will appear due to a value being too close to zero. If the far clipping plane is too large a value, a numerical error is also encountered, especially if the near clipping plane is too small a value.

Y-down versus Y-up

Most 2D graphics coordinate systems (including UI) have Y increasing as you go down the screen. This is true of PhotoShop, JavaFX, and Illustrator. Basically most 2D packages work this way. Many 3D graphics coordinate systems typically have Y increasing as you move up the screen. Some 3D graphics coordinate systems have Z increasing as you move up, but most have Y increasing as you move up the screen.

Y down versus Y up are both correct in their own context. In JavaFX, the camera's coordinate system is Y-down, which means X axis points to the right, Y axis is pointing down, Z axis is pointing away from the viewer or into the screen.

If you want to think of a 3D scene as Y-up, you could create an Xform node, called root3D, under root, as shown in Example 3-2. You set its rx.setAngle property to 180 degrees, basically turning it upside down. Then, add your 3D elements to your root3D node and put your camera under root3D.

Example 3-2 Create Xform node, root3D

root3D = new Xform();
root3D.rx.setAngle(180.0);
root.getChildren().add(root3D);
root3D.getChildren().add(...); // add all your 3D nodes here

You can also create an Xform node called cameraXform and put it under the root, as shown in Example 3-3. You turn it upside down, and put your camera under the cameraXform.

Example 3-3 Create a cameraXform Node

Camera camera = new PerspectiveCamera(true);
Xform cameraXform = new Xform();
root.getChildren().add(cameraXform);
cameraXform.getChildren().add(camera);
cameraXform.rz.setAngle(180.0);

An even better way, with subtle difference on the camera node, is to add a 180 degree rotation to the camera. The rotation used is not the one provided for you because you want to avoid the auto pivoting. In Example 3-4, the camera is turned 180 degrees and it is then added to the camera as a child of cameraXform. The subtle difference is that the cameraXform retains very pristine values and in its default position, everything is zeroed out, including the translations and rotations.

Example 3-4 Create cameraXform and Rotate

Camera camera = new PerspectiveCamera(true);
Xform cameraXform = new Xform();
root.getChildren().add(cameraXform);
cameraXform.getChildren().add(camera);
Rotate rz = new Rotate(180.0, Rotate.Z_AXIS);
camera.getTransforms().add(rz);

Sample Code Using PerspectiveCamera

The Simple3DBoxApp sample, shown in Example 3-5, creates a 3D box and uses a perspective camera for rendering the scene. This sample application is part of the Ensemble 8 Samples that you can download from the JavaFX Demos and Samples section at http://www.oracle.com/technetwork/java/javase/downloads/.

The MSAAApp.java application also gives an example of how to use the Camera API.

Example 3-5 3D Box Sample Application

package simple3dbox;
 
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
 
public class Simple3DBoxApp extends Application {
 
    public Parent createContent() throws Exception {
 
        // Box
        Box testBox = new Box(5, 5, 5);
        testBox.setMaterial(new PhongMaterial(Color.RED));
        testBox.setDrawMode(DrawMode.LINE);
 
        // Create and position camera
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.getTransforms().addAll (
                new Rotate(-20, Rotate.Y_AXIS),
                new Rotate(-20, Rotate.X_AXIS),
                new Translate(0, 0, -15));
 
        // Build the Scene Graph
        Group root = new Group();       
        root.getChildren().add(camera);
        root.getChildren().add(testBox);
 
        // Use a SubScene       
        SubScene subScene = new SubScene(root, 300,300);
        subScene.setFill(Color.ALICEBLUE);
        subScene.setCamera(camera);
        Group group = new Group();
        group.getChildren().add(subScene);
        return group;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setResizable(false);
        Scene scene = new Scene(createContent());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * Java main for when running without JavaFX launcher
     */
    public static void main(String[] args) {
        launch(args);
    }
}
Close Window

Table of Contents

JavaFX: Working with JavaFX Graphics

Expand | Collapse