您如何像此处的JavaFX 2.0演示中那样创建视频墙:

https://www.youtube.com/watch?v=UXSmJYFrulY#t=411

首先,它不必是视频,也可以是图像。我只想像视频中那样放置节点,即e。呈弯曲的形状,如圆柱体或球体的内部。

还是该演示的来源在某处可用?

非常感谢你。

最佳答案

我研究并找到了一个非常棒的网站,其中提供了相关信息:

http://paulbourke.net/geometry/transformationprojection/

相关部分是坐标系转换,尤其是用于在笛卡尔坐标和球形坐标之间转换的方程式。

double x = r * Math.sin(angle1) * Math.cos(angle2);
double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);

在下面的示例中,公式中不使用y,因为图像行是堆叠在一起的。

注意:通过在从-Math.PI到Math.PI的2个嵌套的for循环中使用这些公式,可以对球体周围的节点进行布局。关于整个球面,最困难的部分是将节点向中心旋转,这是我不知道的。

因为我不熟悉Java3D,所以我还 checkout 了Building a 3D Sample Application

最后,我得到了一个视频墙,代码简化为:
public class VideoWall extends Application {

    Random rand = new Random();

    Group root = new Group();
    PerspectiveCamera camera;

    private static final double CAMERA_INITIAL_DISTANCE = -850;
    private static final double CAMERA_NEAR_CLIP = 0.1;
    private static final double CAMERA_FAR_CLIP = 10000.0;

    Image[] images = new Image[] {
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg"),
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/White_Lion.jpg/320px-White_Lion.jpg"),
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Lion_female.jpg/319px-Lion_female.jpg")

    };

    public VideoWall(){

    }
    public static void main(String[] args) {
        launch(args);
    }

    /**
     * Create ImageView with random Image.
     * @return
     */
    private ImageView createImageView() {

        Image image = images[ rand.nextInt(images.length)];

        ImageView c = new ImageView( image);

        c.setFitWidth(140);
        c.setFitWidth(100);
        c.setPreserveRatio(true);

        return c;
    }

    @Override
    public void start(Stage primaryStage) {

        // build camera
        camera = new PerspectiveCamera(true);
        camera.setNearClip(CAMERA_NEAR_CLIP);
        camera.setFarClip(CAMERA_FAR_CLIP);
        camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);

        // we display any node (imageview, webview, etc)
        Node node;

        // create a single webview; we only add it once because we don't want to flood youtube
        WebView webView = new WebView();
        webView.getEngine().load(
          "http://www.youtube.com/embed/utUPth77L_o?autoplay=1"
        );
        webView.setPrefSize(100, 70);

        // wall. the degrees depend on the distance, image size, translate start points, etc. so these values were just as they fit
        double ringBeginDeg = -30;
        double ringEndDeg = 38;

        double r = 1300;
        double yOffset = 80; // offset per image row
        double yOffsetInitial = 120; // initial y offset from "floor"

        int count=0;

        for( double angle1=Math.toRadians(ringBeginDeg); angle1 <Math.toRadians(ringEndDeg); angle1+=0.08)
        {

            double angle2 = Math.PI;

            for( int i=-3; i <= 3; i++)
            {

                double x = r * Math.sin(angle1) * Math.cos(angle2);
                // double y = r * Math.sin(angle1) * Math.sin(angle2);
                double z = r * Math.cos(angle1);

                // add 1 webview, the rest imageviews
                if( count == 16) {
                    node = webView;
                } else {
                    node = createImageView();
                }

                node.setTranslateX(x);
                node.setTranslateY(yOffset * i - yOffsetInitial);
                node.setTranslateZ(z);

                // rotate towards viewer position
                Rotate rx = new Rotate();
                rx.setAxis(Rotate.Y_AXIS);
                rx.setAngle(Math.toDegrees( -angle1));

                node.getTransforms().addAll(rx);

                root.getChildren().add( node);

                count++;
            }

        }

        Scene scene = new Scene(root, 1600, 900, Color.BLACK);

        primaryStage.setScene( scene);
        primaryStage.show();

        scene.setCamera(camera);
    }

}

您可以添加所需的任何节点。我添加了一个YouTube Webview进行测试。它可以播放,但是视频没有加载,因此您所看到的只是静噪(屏幕截图中的灰色图块)。因此,从理论上讲,您可以使所有节点都带有youtube视频进行网络浏览,但这意味着youtube泛滥。更好地使用一些离线视频。

这是屏幕截图:

我还玩了完整的3d示例并制作了戒指。从外部 View 看,它就是这样(始终具有相同的图像):

将相机置于中央,可以很好地滚动环。

如果有人想在附近玩耍,请here's a quick & dirty gist with the navigable ring。使用鼠标左/右/中键进行导航。

如果您想在一个完整的球体周围玩耍,可以使用以下方法:
// full sphere
for (double angle1 = -Math.PI; angle1 <= Math.PI; angle1 += 0.15) {
    for (double angle2 = -Math.PI; angle2 <= Math.PI; angle2 += 0.15) {

        double x = r * Math.sin(angle1) * Math.cos(angle2);
        double y = r * Math.sin(angle1) * Math.sin(angle2);
        double z = r * Math.cos(angle1);

        c = createImageView();

        c.setTranslateX(x);
        c.setTranslateY(y);
        c.setTranslateZ(z);

        Rotate rx = new Rotate();
        rx.setAxis(Rotate.Y_AXIS);
        rx.setAngle(Math.toDegrees(-angle1));

        c.getTransforms().addAll(rx);

        world.getChildren().add(c);
    }
}

看起来像这样:

但是如前所述,我还没有弄清楚如何旋转所有图块以使其向中心看。而且它们需要平均分配。但这只是为了娱乐和偏离主题。

由于这是我问题中视频的一部分,因此只需保留一个并行过渡列表即可创建图块的“堆积”动画。最下面的行现在有一个反射。

扩展代码:
public class VideoWall extends Application {

    Random rand = new Random();

    Group root = new Group();
    PerspectiveCamera camera;

    private static final double CAMERA_INITIAL_DISTANCE = -850;
    private static final double CAMERA_NEAR_CLIP = 0.1;
    private static final double CAMERA_FAR_CLIP = 10000.0;

    Image[] images = new Image[] {
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg"),
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/White_Lion.jpg/320px-White_Lion.jpg"),
            new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Lion_female.jpg/319px-Lion_female.jpg")

    };

    List<ParallelTransition> transitionList = new ArrayList<>();

    public VideoWall(){

    }
    public static void main(String[] args) {
        launch(args);
    }

    /**
     * Create ImageView with random Image.
     * @return
     */
    private ImageView createImageView() {

        Image image = images[ rand.nextInt(images.length)];

        ImageView c = new ImageView( image);

        c.setFitWidth(140);
        c.setFitWidth(100);
        c.setPreserveRatio(true);

        return c;
    }

    @Override
    public void start(Stage primaryStage) {

        // build camera
        camera = new PerspectiveCamera(true);
        camera.setNearClip(CAMERA_NEAR_CLIP);
        camera.setFarClip(CAMERA_FAR_CLIP);
        camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);

        // we display any node (imageview, webview, etc)
        Node node;

        // wall. the degrees depend on the distance, image size, translate start points, etc. so these values were just as they fit
        double ringBeginDeg = -30;
        double ringEndDeg = 38;

        double r = 1300;
        double yOffset = 80; // offset per image row
        double yOffsetInitial = 120; // initial y offset from "floor"

        int min = -3;
        int max = 3;

        for( double angle1=Math.toRadians(ringBeginDeg); angle1 <Math.toRadians(ringEndDeg); angle1+=0.08)
        {

            double angle2 = Math.PI;

            for( int i=min; i <= max; i++)
            {

                double x = r * Math.sin(angle1) * Math.cos(angle2);
                // double y = r * Math.sin(angle1) * Math.sin(angle2);
                double z = r * Math.cos(angle1);

                node = createImageView();

                node.setTranslateX(x);
                node.setTranslateY(yOffset * i - yOffsetInitial);
                node.setTranslateZ(z);

                // rotate towards viewer position
                Rotate rx = new Rotate();
                rx.setAxis(Rotate.Y_AXIS);
                rx.setAngle(Math.toDegrees( -angle1));

                node.getTransforms().addAll(rx);

                // reflection on bottom row
                if( i==max) {
                    Reflection refl = new Reflection();
                    refl.setFraction(0.8f);
                    node.setEffect(refl);
                }

                // build the wall using a transition
                node.setVisible(false);
                transitionList.add( createTransition( node));

                root.getChildren().add( node);

            }

        }

        Scene scene = new Scene(root, 1600, 900, Color.BLACK);

        primaryStage.setScene( scene);
        primaryStage.show();

        scene.setCamera(camera);

        AnimationTimer timer = createAnimation();
        timer.start();

    }

    private AnimationTimer createAnimation() {

        Collections.sort(transitionList, new Comparator<ParallelTransition>() {

            @Override
            public int compare(ParallelTransition arg0, ParallelTransition arg1) {

                // bottom right to top left
                Point2D ref = new Point2D(1000,1000);
                Point2D pt0 = new Point2D( arg0.getNode().getTranslateX(), arg0.getNode().getTranslateY());
                Point2D pt1 = new Point2D( arg1.getNode().getTranslateX(), arg1.getNode().getTranslateY());

                return Double.compare(ref.distance(pt0), ref.distance(pt1));

                // bottom row first
                // return -Double.compare( arg0.getNode().getTranslateY(), arg1.getNode().getTranslateY());

            }

        });


        AnimationTimer timer = new AnimationTimer() {

            long last = 0;

            @Override
            public void handle(long now) {

                //if( (now - last) > 1_000_000_000)
                if( (now - last) >   40_000_000)
                {
                    if( transitionList.size() > 0) {

                        ParallelTransition t = transitionList.remove(0);
                        t.getNode().setVisible(true);
                        t.play();

                    }
                    last = now;

                }

                if( transitionList.size() == 0) {
                    stop();
                }
            }

        };

        return timer;
    }

    private ParallelTransition createTransition( final Node node) {

        Path path = new Path();
        path.getElements().add(new MoveToAbs( node, node.getTranslateX() - 1000, node.getTranslateY() - 900));
        path.getElements().add(new LineToAbs( node, node.getTranslateX(), node.getTranslateY()));

        Duration duration = Duration.millis(1500);

        PathTransition pt = new PathTransition( duration, path, node);

        RotateTransition rt = new RotateTransition( duration, node);
        rt.setByAngle(720);
        rt.setAutoReverse(true);

        ParallelTransition parallelTransition = new ParallelTransition();
        parallelTransition.setNode(node);
        parallelTransition.getChildren().addAll(pt, rt);

        return parallelTransition;

    }

    public static class MoveToAbs extends MoveTo {

        public MoveToAbs( Node node, double x, double y) {
            super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
        }

    }

    public static class LineToAbs extends LineTo {

        public LineToAbs( Node node, double x, double y) {
            super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
        }

    }

}

09-30 16:54
查看更多