JavaFX 全景查看器的照明

Lighting for JavaFX pano viewer

我目前正在尝试使用其 3D 功能在 JavaFX 中设置全景查看器。我想我得到了正确的几何图形(3D 子场景、TriangleMesh 等),但我正在与适当的照明作斗争。我需要的实际上是根本没有照明,只是纹理图像原始颜色的几何正确显示。如果没有指定明确的光照,JavaFX 似乎引入了一些不需要的默认光照,这让我抓狂。

谁能告诉我如何设置 material 和光源以及其他相关的东西,如果我根本不想有任何照明而只想要普通图像(纹理)颜色?

编辑: 这是请求的代码示例。当盒子旋转时,由于光照,您可以清楚地看到盒子的角。如果你能关掉灯光,这些角落应该会消失。

import javafx.animation.Interpolator;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SimplePanoViewer extends Application {

  private static final int VIEWPORT_SIZE = 1200;

  private static final double MODEL_SCALE_FACTOR = VIEWPORT_SIZE/2;

  private static final String textureLoc = "http://www.f-lohmueller.de/pov_tut/backgrnd/im/Cubemap_2_2048x1536.jpg";

  private Image texture;
  private PhongMaterial texturedMaterial = new PhongMaterial();

  private MeshView meshView = loadMeshView();

  private MeshView loadMeshView() {

    float[] points = {
            -0.5f, -0.5f, -0.5f,
            -0.5f, +0.5f, -0.5f,
            +0.5f, +0.5f, -0.5f,
            +0.5f, -0.5f, -0.5f,

            -0.5f, -0.5f, +0.5f,
            -0.5f, +0.5f, +0.5f,
            +0.5f, +0.5f, +0.5f,
            +0.5f, -0.5f, +0.5f
    };

    float TX0 = 0f;
    float TX1 = 1f/4f;
    float TX2 = 2f/4f;
    float TX3 = 3f/4f;
    float TX4 = 1f;

    float TY0 = 0f;
    float TY1 = 1f/3f;
    float TY2 = 2f/3f;
    float TY3 = 1f;

    float[] texCoords = {
            TX0, TY1,
            TX0, TY2,
            TX1, TY2,
            TX1, TY3,
            TX2, TY3,
            TX2, TY2,
            TX3, TY2,
            TX4, TY2,
            TX4, TY1,
            TX3, TY1,
            TX2, TY1,
            TX2, TY0,
            TX1, TY0,
            TX1, TY1,
        };
    int[] faces = {
            0, 0, 1, 1, 5, 2,  5, 2, 4, 13, 0, 0,           // 0
            4, 13, 5, 2, 6, 5,  6, 5, 7, 10, 4, 13,         // 1
            7, 10, 6, 5, 2, 6,  2, 6, 3, 9, 7, 10,          // 2
            3, 9, 2, 6, 1, 7,  1, 7, 0, 8, 3, 9,            // 3
            0, 12, 4, 13, 7, 10,  7, 10, 3, 11, 0, 12,      // 4
            5, 2, 1, 3, 2, 4,  2, 4, 6, 5, 5, 2             // 5
    };

    TriangleMesh mesh = new TriangleMesh();
    mesh.getPoints().setAll(points);
    mesh.getTexCoords().setAll(texCoords);
    mesh.getFaces().setAll(faces);

    return new MeshView(mesh);
  }

  private Group buildScene() {
    meshView.setTranslateX(VIEWPORT_SIZE / 2);
    meshView.setTranslateY(VIEWPORT_SIZE / 2 * 9.0 / 16);
    meshView.setTranslateZ(-VIEWPORT_SIZE );
    meshView.setScaleX(MODEL_SCALE_FACTOR);
    meshView.setScaleY(MODEL_SCALE_FACTOR);
    meshView.setScaleZ(MODEL_SCALE_FACTOR);

    return new Group(meshView);
  }

  @Override
  public void start(Stage stage) {
    texture = new Image(textureLoc);
    texturedMaterial.setDiffuseMap(texture);

    Group group = buildScene();

    RotateTransition rotate = rotate3dGroup(group);

    meshView.setCullFace(CullFace.NONE);
    meshView.setDrawMode(DrawMode.FILL);
    rotate.play();
    meshView.setMaterial(texturedMaterial);

    VBox layout = new VBox(
        createScene3D(group)
    );

    stage.setTitle("Model Viewer");

    Scene scene = new Scene(layout, Color.CORNSILK);
    stage.setScene(scene);
    stage.show();
  }

  private SubScene createScene3D(Group group) {
    SubScene scene3d = new SubScene(group, VIEWPORT_SIZE, VIEWPORT_SIZE * 9.0/16);
    scene3d.setFill(Color.rgb(10, 10, 40));
    PerspectiveCamera camera = new PerspectiveCamera();
    scene3d.setCamera(camera);
    return scene3d;
  }

  private RotateTransition rotate3dGroup(Group group) {
    RotateTransition rotate = new RotateTransition(Duration.seconds(30), group);
    rotate.setAxis(Rotate.Y_AXIS);
    rotate.setFromAngle(0);
    rotate.setToAngle(360);
    rotate.setInterpolator(Interpolator.LINEAR);
    rotate.setCycleCount(RotateTransition.INDEFINITE);

    return rotate;
  }

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

我不是 JavaFX 3D 的专家,但我会尽力回答。 JavaFX 3D 需要光才能显示透视图。如果你关掉灯,你的子场景会呈现黑色。

JavaFX 中只有两种光源,AmbientLight 和 PointLight。两者都可以添加到添加网格视图的组中。 JavaDoc 说 AmbientLight:

Ambient light is a light source that seems to come from all directions

PointLight 得到了这样的描述:

A light source that has a fixed point in space and radiates light equally in all directions away from itself.

如果没有灯光被添加到场景或子场景,它会添加一个默认的点光源,从顶部照射到 3D 形状上。如果您添加一个光源(环境光或点光源),则默认的光源将被移除。

如果您想放置一盏灯,您应该在构建场景方法中进行。

  private Group buildScene() {
    meshView.setTranslateX(VIEWPORT_SIZE / 2);
    meshView.setTranslateY(VIEWPORT_SIZE / 2 * 9.0 / 16);
    meshView.setTranslateZ(-VIEWPORT_SIZE);
    meshView.setScaleX(MODEL_SCALE_FACTOR);
    meshView.setScaleY(MODEL_SCALE_FACTOR);
    meshView.setScaleZ(MODEL_SCALE_FACTOR);
    meshView.setCullFace(CullFace.NONE);
    Group group = new Group(meshView);

    AmbientLight ambient = new AmbientLight(); // default color white
    ambient.setLightOn(true); // switch it off and everything is black

    group.getChildren().add(ambient);
    return group;
  }

下面显示的灯是默认灯、PointerLight 和 AmbientLight。正如你所看到的,如果你设置了环境光,透视就完全消失了。

测试代码:

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*;
import javafx.stage.Stage;

public class Shapes3DViewer extends Application {

  PhongMaterial material;

  @Override
  public void start(Stage stage) {
    material = new PhongMaterial();
    material.setDiffuseColor(Color.FIREBRICK);
    material.setSpecularColor(Color.YELLOW);

    PointLight pointLight = new PointLight(Color.WHITE);
    pointLight.setTranslateX(100);
    pointLight.setTranslateY(100);
    pointLight.setTranslateZ(-300);
    pointLight.setRotate(90);

    AmbientLight ambient = new AmbientLight();

    Group g1 = createSphereGroup(200, "Default light");
    Group g2 = createSphereGroup(200, "Point light");
    Group g3 = createSphereGroup(200, "Ambient light");

    g2.getChildren().add(pointLight);
    g3.getChildren().add(ambient);

    SubScene s1 = createSubScene(g1, 500, 500);
    SubScene s2 = createSubScene(g2, 500, 500);
    SubScene s3 = createSubScene(g3, 500, 500);

    HBox root = new HBox();
    root.getChildren().addAll(s1, s2, s3);

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
  }

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

  private Group createSphereGroup(double radius, String text) {
    Sphere c = new Sphere(radius);
    c.setMaterial(material);
    c.setDrawMode(DrawMode.FILL);
    c.setTranslateX(radius * 1.33);
    c.setTranslateY(radius * 1.33);
    Label lbl = new Label(text);
    lbl.setStyle("-fx-text-fill: red;-fx-font-size: 18pt;");
    return new Group(c, lbl);
  }

  private SubScene createSubScene(Group group, double width, double height) {
    SubScene s = new SubScene(group, width, height);
    s.setCamera(new PerspectiveCamera());
    s.setFill(Color.color(.1, .1, .1));
    return s;
  }
}