繁体   English   中英

计算透视相机JavaFX的视口

[英]Computing the Viewport of a Perspective Camera JavaFX

您将如何在JavaFX中计算透视相机的视口的x和y边界? 方法“ camera.getBoundsInParent”可用于确定相机的位置,但是,如何使用该位置来计算相机视口的大小?

根据PerspectiveCamera文档

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

fieldOfView属性定义场景的可见部分与摄影机对着的角度,默认情况下,该角度是垂直测量的

最后,默认情况下,

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

换句话说, 在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))

在右下角,其中zz坐标, w是场景的宽度, h是场景的高度, f是视场的(垂直)角度。

这是一个演示。 它具有四个球体,分别位于场景可见部分的顶部中心,底部中心,左侧中心和右侧中心。 所有四个球体的z坐标都具有动画效果(因此它们在z方向上从查看器移开,似乎变小了),并且它们的xy坐标已绑定,因此它们保持在可见的极限位置场景的一部分。 (因此,例如,红色球体沿着上图顶部的直角三角形的斜边进行动画处理,其他球体也进行类似的动画处理。)

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

在此处输入图片说明

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM