使 3D 对象在 JavaFX 中随着时间的推移而不同地照亮

Make 3D object illuminate differently over time in JavaFX

需要随着时间的推移在 JavaFX "blink" 中制作一个球体,例如淡入和淡出。可能吗?或者至少改变颜色的深浅。我设法通过更改我正在使用的 PhongMaterial 的 SelfIlluminationMap 属性 使其闪烁,代码

import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import javafx.util.Duration;

public class JavaFXApplication15 extends Application {

    Image im = new Image("bump.jpg");
    PhongMaterial ph = new PhongMaterial(Color.GREEN);
    int nums = 0;

    UpdateTimer timer;

    private class UpdateTimer extends AnimationTimer {

        int counter = 0;

        @Override
        public void handle(long now) {
            if (counter == 20) {
                update();
                counter = 0;
            }
            counter++;

        }
    }

    private Parent createContent() throws Exception {

        timer = new UpdateTimer();
        ph = new PhongMaterial(Color.YELLOW, null, null, null, im);

        Box box = new Box(5, 5, 5);
        box.setMaterial(ph);

        // Create and position camera
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.getTransforms().addAll(
                new Rotate(-20, Rotate.X_AXIS),
                new Translate(0, 0, -50)
        );

        // Build the Scene Graph
        Group root = new Group();
        root.getChildren().add(camera);
        root.getChildren().add(box);

        // Use a SubScene
        SubScene subScene = new SubScene(
                root,
                300, 300,
                true,
                SceneAntialiasing.BALANCED
        );
        subScene.setFill(Color.ALICEBLUE);
        subScene.setCamera(camera);
        Group group = new Group();
        group.getChildren().add(subScene);

        return group;
    }

    void update() {

        if (ph.getSelfIlluminationMap() != null) {
            ph.setSelfIlluminationMap(null);
        } else {
            ph.setSelfIlluminationMap(im);
        }

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setResizable(false);
        Scene scene = new Scene(createContent());
        stage.setScene(scene);
        stage.show();
        timer.start();
    }

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

但是有没有可能让它淡入淡出呢?可能有某种过渡?

为了查看效果,我尝试为球体 PhongMaterialspecularColor 制作动画。从此 example, I followed the approach shown here 开始,对给定色调的等间距亮度值进行颜色查找 table。

private final Queue<Color> clut = new LinkedList<>();

handle() 的实现简单地循环通过 table。

@Override
public void handle(long now) {
    redMaterial.setSpecularColor(clut.peek());
    clut.add(clut.remove());
}

如果结果有吸引力,使用 Transition.

的具体子类可能会产生更灵活的方法
private final Animation animation = new Transition() {

     {
         setCycleDuration(Duration.millis(1000));
         setAutoReverse(true);
         setCycleCount(INDEFINITE);
     }

     @Override
     protected void interpolate(double d) {
         redMaterial.setSpecularColor(Color.hsb(LITE.getHue(), 1, d));
         redMaterial.setDiffuseColor(Color.hsb(DARK.getHue(), 1, d / 2));
     }
 };

import java.util.LinkedList;
import java.util.Queue;
import javafx.animation.AnimationTimer;
import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @see 
 * @see 
 * @see 
 * @see 
 */
public class TriadBox extends Application {

    private static final double SIZE = 300;
    private final Content content = Content.create(SIZE);
    private double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;

    private static final class Content {

        private static final double WIDTH = 3;
        private static final Color LITE = Color.RED;
        private static final Color DARK = Color.RED.darker().darker();
        private final Xform group = new Xform();
        private final Group cube = new Group();
        private final Group axes = new Group();
        private final Box xAxis;
        private final Box yAxis;
        private final Box zAxis;
        private final Box box;
        private final Sphere sphere;
        private final PhongMaterial redMaterial = new PhongMaterial();
        private final UpdateTimer timer = new UpdateTimer();

        private class UpdateTimer extends AnimationTimer {
            private static final double N = 32d;
            private final Queue<Color> clut = new LinkedList<>();

            public UpdateTimer() {
                for (int i = 0; i < N; i++) {
                    clut.add(Color.hsb(LITE.getHue(), 1, 1 - (i / N)));
                }
                for (int i = 0; i < N; i++) {
                    clut.add(Color.hsb(LITE.getHue(), 1, i / N));
                }
            } 

            @Override
            public void handle(long now) {
                redMaterial.setSpecularColor(clut.peek());
                clut.add(clut.remove());
            }
        }

        private final Animation animation = new Transition() {

             {
                 setCycleDuration(Duration.millis(1000));
                 setAutoReverse(true);
                 setCycleCount(INDEFINITE);
             }

             @Override
             protected void interpolate(double d) {
                 redMaterial.setSpecularColor(Color.hsb(LITE.getHue(), 1, d));
                 redMaterial.setDiffuseColor(Color.hsb(DARK.getHue(), 1, d / 2));
             }
         };

        private static Content create(double size) {
            Content c = new Content(size);
            c.cube.getChildren().addAll(c.box, c.sphere);
            c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
            c.group.getChildren().addAll(c.cube, c.axes);
            return c;
        }

        private Content(double size) {
            double edge = 3 * size / 4;
            xAxis = createBox(edge, WIDTH, WIDTH, edge);
            yAxis = createBox(WIDTH, edge / 2, WIDTH, edge);
            zAxis = createBox(WIDTH, WIDTH, edge / 4, edge);
            box = new Box(edge, edge / 2, edge / 4);
            box.setDrawMode(DrawMode.LINE);
            sphere = new Sphere(8);
            redMaterial.setDiffuseColor(DARK);
            redMaterial.setSpecularColor(LITE);
            sphere.setMaterial(redMaterial);
            sphere.setTranslateX(edge / 2);
            sphere.setTranslateY(-edge / 4);
            sphere.setTranslateZ(-edge / 8);
        }

        private Box createBox(double w, double h, double d, double edge) {
            Box b = new Box(w, h, d);
            b.setMaterial(new PhongMaterial(Color.AQUA));
            b.setTranslateX(-edge / 2 + w / 2);
            b.setTranslateY(edge / 4 - h / 2);
            b.setTranslateZ(edge / 8 - d / 2);
            return b;
        }
    }

    private static class Xform extends Group {

        private final Point3D px = new Point3D(1.0, 0.0, 0.0);
        private final Point3D py = new Point3D(0.0, 1.0, 0.0);
        private Rotate r;
        private Transform t = new Rotate();

        public void rx(double angle) {
            r = new Rotate(angle, px);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }

        public void ry(double angle) {
            r = new Rotate(angle, py);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }

        public void rz(double angle) {
            r = new Rotate(angle);
            this.t = t.createConcatenation(r);
            this.getTransforms().clear();
            this.getTransforms().addAll(t);
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("JavaFX 3D");
        Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
        primaryStage.setScene(scene);
        scene.setFill(Color.BLACK);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setFarClip(SIZE * 6);
        camera.setTranslateZ(-2 * SIZE);
        scene.setCamera(camera);
        scene.setOnMousePressed((MouseEvent e) -> {
            mousePosX = e.getSceneX();
            mousePosY = e.getSceneY();
            mouseOldX = e.getSceneX();
            mouseOldY = e.getSceneY();
        });
        scene.setOnMouseDragged((MouseEvent e) -> {
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
            mousePosX = e.getSceneX();
            mousePosY = e.getSceneY();
            mouseDeltaX = (mousePosX - mouseOldX);
            mouseDeltaY = (mousePosY - mouseOldY);
            if (e.isShiftDown()) {
                content.group.rz(-mouseDeltaX * 180.0 / scene.getWidth());
            } else if (e.isPrimaryButtonDown()) {
                content.group.rx(+mouseDeltaY * 180.0 / scene.getHeight());
                content.group.ry(-mouseDeltaX * 180.0 / scene.getWidth());
            } else if (e.isSecondaryButtonDown()) {
                camera.setTranslateX(camera.getTranslateX() - mouseDeltaX * 0.1);
                camera.setTranslateY(camera.getTranslateY() - mouseDeltaY * 0.1);
            }
        });
        scene.setOnScroll((final ScrollEvent e) -> {
            camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
        });
        primaryStage.show();
        //content.timer.start();
        content.animation.play();
    }

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