Вычисление области обзора камеры перспективы JavaFX

как бы вы рассчитали границы x и y области просмотра перспективной камеры в JavaFX? Метод camera.getBoundsInParent может использоваться для определения положения камеры, однако как вы можете использовать это положение для расчета размера окна просмотра камеры?


person Politic Revolutionnaire    schedule 18.12.2017    source источник
comment
Конечно, в перспективе границы видимых координат x и y будут зависеть от координаты z?   -  person James_D    schedule 19.12.2017
comment
На самом деле, однако, я не уверен, как перевести информацию о положении, размере экрана и т. Д. В границы.   -  person Politic Revolutionnaire    schedule 19.12.2017
comment
Свойство fieldOfView дает вам угол; вы можете вычислить это из этого.   -  person James_D    schedule 19.12.2017
comment
Вопрос в том, как именно вычисление будет работать и почему оно работает.   -  person Politic Revolutionnaire    schedule 19.12.2017


Ответы (1)


Согласно документации для PerspectiveCamera:

По умолчанию эта камера расположена в центре сцены и смотрит вдоль положительной оси Z. Система координат, определяемая этой камерой, имеет начало в верхнем левом углу панели с осью Y, направленной вниз, и осью Z, направленной от зрителя (в экран).

Свойство fieldOfView определяет угол подчиняется видимой части сцены камере, и по умолчанию этот угол составляет измерено по вертикали.

Наконец, по умолчанию

значение Z положения глаза регулируется в Z таким образом, что матрица проекции, сгенерированная с использованием указанного fieldOfView, будет создавать единицы с Z = 0 (плоскость проекции) в независимых от устройства пикселях, совпадающих с таковой для ParallelCamera

Другими словами, видимая часть сцены при Z = 0 имеет размер сцены в пиксельных координатах.

Итак, принимая эти значения по умолчанию, мы можем нарисовать следующую диаграмму:

введите здесь описание изображения

Здесь точка слева представляет положение камеры по умолчанию. Прямоугольник - это видимая часть экрана в позиции 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)

or

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

введите здесь описание изображения

Если вы добавите камеру в сцену и переместите (и, возможно, поверните) ее, тогда геометрия станет значительно более сложной, хотя базовое изображение все равно останется прежним (только неудобно выровнять с осями). Подобные вычисления для более общего случая выходят за рамки сообщения на форуме (и оставлены в качестве упражнения для читателя ...).

person James_D    schedule 19.12.2017