计算透视相机JavaFX的视口

问题描述 投票:0回答:1

如何计算JavaFX中透视摄像机视口的x和y界限?方法'camera.getBoundsInParent'可用于确定摄像机的位置,但是,如何使用该位置计算摄像机视口的大小?

java javafx viewport bounds perspectivecamera
1个回答
0
投票

根据documentation for PerspectiveCamera

默认情况下,此摄像机位于场景的中心,沿正z轴显示。此摄像机定义的坐标系统的原点位于面板的左上角,Y轴朝下,Z轴指向远离观察者(进入屏幕)。

fieldOfView property将场景的可见部分所对应的角度定义为相机,默认情况下,此角度为measured vertically

最后,默认情况下,

在Z中调整眼睛位置的Z值,使得使用指定的fieldOfView生成的投影矩阵将产生Z = 0(投影平面)的单位,在与设备无关的像素中,与ParallelCamera的单位匹配

换句话说,Z = 0处的场景的可见部分以像素坐标固定为场景的大小。

因此,假设这些默认值,我们可以绘制以下图表:

enter image description here

这里,左侧的点代表默认的摄像机位置。矩形是z=0屏幕的可见部分。 w是场景的宽度,h是场景的高度(因此相机在某些(w/2, h/2, -z_c)z_c > 0)。

来自相机的线在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))

在右下角,其中zz坐标,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);
    }
}

enter image description here

如果将相机添加到场景中并移动(并可能旋转)它,那么几何图形会变得相当复杂,尽管基本图像仍然是相同的(只是不方便地与轴对齐)。对于更一般情况的类似计算超出了论坛帖子的范围(并留给读者练习......)。

© www.soinside.com 2019 - 2024. All rights reserved.