文档



JavaFX:使用JavaFX图形

3 相机

本章介绍了包含在JavaFX 3D图形功能中的相机API。

相机现在是一个可以添加到JavaFX场景图中的节点,因此允许您在3D UI布局中移动相机。这与2D布局不同,其中相机保持在一个位置。

在JavaFX场景坐标空间中,默认相机的投影平面位于Z=0,相机坐标系统如下:

• X轴指向右侧

• Y轴指向下方

• Z轴指向远离观察者或进入屏幕。

3 相机(Release 8)

透视相机

JavaFX提供了透视相机用于渲染3D场景。该相机定义了透视投影的视图体积。可以通过改变fieldOfView属性的值来改变视图体积。

示例3-1展示了创建透视相机的两个构造函数:

示例3-1 透视相机的构造函数

PerspectiveCamera()

PerspectiveCamera(boolean fixedEyeAtCameraZero)

后一个构造函数是JavaFX 8中的新构造函数,允许您使用指定的fixedEyeAtCameraZero标志来控制相机位置,以便渲染3D环境中相机所看到的内容。

应该使用以下构造函数进行3D图形编程:

PerspectiveCamera(true);

当选项fixedEyeAtCameraZero设置为true时,透视相机在其坐标空间中的眼睛位置固定在(0, 0, 0),无论投影区域的尺寸变化或窗口调整大小。

fixedEyeAtCameraZero设置为默认值false时,相机定义的坐标系统的原点位于面板的左上角。这种模式用于使用透视相机渲染的2D UI控件,但对于大多数3D图形应用程序来说并不实用。例如,当窗口调整大小时,相机会移动,以保持原点位于面板的左上角。这对于2D UI布局是正确的,但对于3D布局则不是。因此,在进行3D图形的转换或移动相机时,重要的是记住将fixedEyeAtCameraZero属性设置为true

要创建相机并将其添加到场景中,请使用以下代码:

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

使用以下代码将相机添加到场景图中。

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

要旋转相机并移动cameraGroup,请使用以下代码:

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

视野

可以通过以下方式设置相机的视野:

camera.setFieldOfView(double value);

视野越大,透视畸变和尺寸差异越大。

  • 鱼眼镜头的视野最高可达180度以上。

  • 普通镜头的视野在40到62度之间。

  • 长焦镜头的视野为1度(或更小)到30度。

剪裁平面

您可以在本地坐标系中设置相机的近剪裁平面,如下所示:

camera.setNearClip(double value);

要设置相机的远剪裁平面,请使用以下代码:

camera.setFarClip(double value);

设置近剪裁平面或远剪裁平面将确定视图体积。如果近剪裁平面太大,它将开始剪裁场景的前部。如果太小,则会开始剪裁场景的后部。

提示:

不要将近剪裁值设置为比所需值更小的值,也不要将远剪裁值设置为比所需值更大的值,否则可能会出现奇怪的视觉伪影。

剪裁平面需要设置得足够大,以便能够看到足够的场景。但是,视图范围不应设置得太大,以免遇到数值错误。如果近剪裁平面的值太大,场景将开始被剪裁。但是,如果近剪裁平面太小,由于值太接近零,将出现不同类型的视觉伪影。如果远剪裁平面的值太大,也会遇到数值错误,尤其是如果近剪裁平面的值太小。

Y-down与Y-up

大多数2D图形坐标系统(包括UI)的Y轴向下递增。这适用于PhotoShop、JavaFX和Illustrator等大多数2D软件包。许多3D图形坐标系统通常Y轴向上递增。一些3D图形坐标系统的Z轴向上递增,但大多数情况下Y轴向上递增。

Y-down和Y-up在各自的上下文中都是正确的。在JavaFX中,相机的坐标系统是Y-down,这意味着X轴指向右侧,Y轴指向下方,Z轴指向远离观察者或进入屏幕。

如果你想将一个3D场景视为Y-up,你可以在根节点下创建一个名为root3DXform节点,如示例3-2所示。将其rx.setAngle属性设置为180度,基本上将其颠倒过来。然后,将你的3D元素添加到root3D节点中,并将相机放在root3D下。

示例3-2 创建Xform节点root3D

root3D = new Xform();
root3D.rx.setAngle(180.0);
root.getChildren().add(root3D);
root3D.getChildren().add(...); // 在这里添加所有的3D节点

你还可以创建一个名为cameraXform的Xform节点,并将其放在根节点下,如示例3-3所示。将其颠倒过来,并将相机放在cameraXform下。

示例3-3 创建一个cameraXform节点

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

更好的方法是在相机节点上添加一个180度的旋转,这样可以避免自动旋转。在示例3-4中,相机被旋转180度,然后作为cameraXform的子节点添加到相机中。微妙的区别在于cameraXform保留了非常原始的值,在其默认位置下,包括平移和旋转都被归零。

示例3-4 创建cameraXform和旋转

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);

使用PerspectiveCamera的示例代码

示例代码Simple3DBoxApp,如示例3-5所示,创建了一个3D盒子,并使用透视相机来渲染场景。这个示例应用程序是Ensemble 8 Samples的一部分,您可以从JavaFX Demos and Samples部分的http://www.oracle.com/technetwork/java/javase/downloads/下载。

应用程序MSAAApp.java也提供了如何使用Camera API的示例。

示例3-5 3D盒子示例应用程序

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 testBox = new Box(5, 5, 5);
        testBox.setMaterial(new PhongMaterial(Color.RED));
        testBox.setDrawMode(DrawMode.LINE);
 
        // 创建并定位相机
        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));
 
        // 构建场景图
        Group root = new Group();       
        root.getChildren().add(camera);
        root.getChildren().add(testBox);
 
        // 使用子场景       
        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主函数,用于在没有JavaFX启动器的情况下运行
     */
    public static void main(String[] args) {
        launch(args);
    }
}
关闭窗口

目录

JavaFX: 使用JavaFX图形

展开 折叠