根据documentation for PerspectiveCamera:
默认情况下,此摄像机位于场景中心,并沿 Z 轴正方向观察。此相机定义的坐标系原点位于面板的左上角,Y 轴指向下方,Z 轴指向远离观察者(进入屏幕)。
fieldOfView property 定义了场景可见部分对摄像机的角度,默认情况下该角度为measured vertically。
最后,默认情况下,
眼睛位置的 Z 值在 Z 中进行调整,以便使用指定的fieldOfView 生成的投影矩阵将在 Z = 0(投影平面)处生成与设备无关的像素的单位,与 @ 的匹配987654331@
换句话说,场景的可见部分Z=0固定在像素坐标中的场景大小。
所以,假设这些默认值,我们可以画出下图:
这里,左边的点代表默认的相机位置。矩形是z=0 处屏幕的可见部分。 w 是场景的宽度,h 是场景的高度(因此对于某些z_c > 0,相机位于(w/2, h/2, -z_c))。
在 z=0 处与场景可见部分顶部中心相交的相机线在图中以任意 z 值延伸到场景可见部分顶部中心的某个点。很容易看出,上面的三角形是一个直角三角形,高-y,长z,类似于从相机到场景中心z=0形成的直角三角形。由于三角形相似,它有角度f/2,其中f 是视场角。因此,对于场景可见部分顶部的点(w/2, y, z),我们必须有
-y/z = tan(f/2)
或
y = -z tan(f/2)
你可以在图片底部再建一个三角形,推导出y坐标满足
y = h + z tan(f/2)
对于宽度,只需注意场景可见部分的比例始终是比例w:h,因此对于位于场景可见部分左边缘中心的点(x, h/2, z),我们有
x = -z (w/h) tan(f/2)
在场景可见部分的右侧
x = w + z (w/h) tan(f/2)
所以,总而言之,场景的边界从
(-z (w/h) tan(f/2), -z tan(f/2))
在左上角,到
(w + z (w/h) tan(f/2), h + z tan(f/2))
在右下角,其中z 是z 坐标,w 是场景的宽度,h 是场景的高度,f 是场的(垂直)角度观点。
这是一个演示。这有四个球体,分别位于场景可见部分的顶部中心、底部中心、左侧中心和右侧中心。所有四个球体的z-坐标都是动画的(因此它们在z-方向上远离观察者,看起来越来越小),并且它们的x-或y-坐标被绑定,以便它们保持在场景可见部分的极端。 (因此,例如,红色球体沿上图顶部直角三角形的斜边进行动画处理,其他球体也以类似方式进行动画处理。)
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PerspectiveCameraTest extends Application {
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Scene scene = new Scene(pane, 800, 800, true);
PerspectiveCamera camera = new PerspectiveCamera();
camera.boundsInParentProperty().addListener((obs, oldB, newB) -> System.out.println(newB));
scene.setCamera(camera);
primaryStage.setScene(scene);
primaryStage.show();
Sphere top = new Sphere(40);
top.setMaterial(new PhongMaterial(Color.RED));
top.translateXProperty().bind(pane.widthProperty().divide(2));
top.translateYProperty().bind(Bindings.createDoubleBinding(() -> {
double tanFOver2 = Math.tan(Math.toRadians(camera.getFieldOfView()/2));
return -top.getTranslateZ() * tanFOver2 ;
}, top.translateZProperty(), pane.heightProperty(), camera.fieldOfViewProperty()));
Sphere bottom = new Sphere(40);
bottom.setMaterial(new PhongMaterial(Color.BLUE));
bottom.translateXProperty().bind(pane.widthProperty().divide(2));
bottom.translateYProperty().bind(Bindings.createDoubleBinding(() -> {
double tanFOver2 = Math.tan(Math.toRadians(camera.getFieldOfView()/2));
return scene.getHeight() + bottom.getTranslateZ() * tanFOver2 ;
}, bottom.translateZProperty(), pane.heightProperty(), camera.fieldOfViewProperty()));
bottom.translateZProperty().bind(top.translateZProperty());
Sphere left = new Sphere(40);
left.setMaterial(new PhongMaterial(Color.GREEN));
left.translateYProperty().bind(pane.heightProperty().divide(2));
left.translateXProperty().bind(Bindings.createDoubleBinding(() -> {
double tanFOver2 = Math.tan(Math.toRadians(camera.getFieldOfView()/2));
return -left.getTranslateZ() * tanFOver2 * pane.getWidth() / pane.getHeight();
}, left.translateZProperty(), pane.heightProperty(), pane.widthProperty(), camera.fieldOfViewProperty()));
left.translateZProperty().bind(top.translateZProperty());
Sphere right = new Sphere(40);
right.setMaterial(new PhongMaterial(Color.GOLD));
right.translateYProperty().bind(pane.heightProperty().divide(2));
right.translateXProperty().bind(Bindings.createDoubleBinding(() -> {
double tanFOver2 = Math.tan(Math.toRadians(camera.getFieldOfView()/2));
return pane.getWidth() + right.getTranslateZ() * tanFOver2 * pane.getWidth() / pane.getHeight() ;
}, right.translateZProperty(), pane.heightProperty(), pane.widthProperty(), camera.fieldOfViewProperty()));
right.translateZProperty().bind(top.translateZProperty());
TranslateTransition anim = new TranslateTransition(Duration.seconds(10), top);
anim.setByZ(5000);
anim.play();
pane.getChildren().addAll(top, bottom, left, right);
}
public static void main(String[] args) {
launch(args);
}
}
如果您将相机添加到场景中并移动(并可能旋转)它,那么几何图形会变得相当复杂,尽管基本图片仍然是相同的(只是不方便与轴对齐)。对于更一般情况的类似计算超出了论坛帖子的范围(留给读者作为练习......)。