[英]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))
在右下角,其中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);
}
}
如果将摄像机添加到场景中并移动(并可能旋转),则几何形状会变得更加复杂,尽管基本图片仍然是相同的(只是不方便地与轴对齐)。 对于更一般的情况,类似的计算超出了论坛的讨论范围(留给读者练习……)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.