-1

I am currently trying to set up a panorama viewer in JavaFX using its 3D features. I think I got the geometry right (3D sub-scene, TriangleMesh, etc.) but I am fighting with the proper lighting. What I need is actually no lighting at all, just the geometrically correct display of the original colors of the texture image. JavaFX seems to introduce some unwanted default lighting if there is no explicit lighting specified and is driving me crazy.

Can anybody tell me how to set up the material and the light sources and maybe other related stuff if I do not want to have any lighting at all but just the plain image (texture) colors?

Edit: Here is the requested code example. When the box is rotating you clearly see the corners of the box due to the lighting. If you could switch off the lighting these corners should disappear.

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);
  }
}
mipa
  • 10,369
  • 2
  • 16
  • 35

1 Answers1

1

I'am not a solid profi on JavaFX 3D, but I'll try to answer. JavaFX 3D needs light to be able to show a perspective view. If you switch light off, your Subscene renders to black.

There are only two Lights in JavaFX, an AmbientLight and a PointLight. Both can be added to the group where the meshview is added. JavaDoc says about AmbientLight:

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

Whereas PointLight got this description:

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

If no Light was added to a Scene or SubScene, it adds a default PointLight that shines from top onto the 3D-Shape. If you add a Light (Ambient or Point) than the default one is removed.

If you would like to place a light, than you should do it in your build scene method.

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

The lights shown below, are the Default light, PointerLight and AmbientLight. As you can see, the perspective is fully gone if you set ambient light.

Lights

Code for testing:

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;
  }
}
aw-think
  • 4,723
  • 2
  • 21
  • 42
  • I don't know why you say that your proposal does not make any difference. It definitely does and solves my problem. (I am using a Mac with 8u60 ea). – mipa Jul 01 '15 at 14:22
  • What really does not seem to matter is setting the fill color of the scene3d to transparent. I've set it to white and the result is still the same. I also do not understand why you translated the ambient light because this light does not depend on its position in contrast to a point light. Also the opacity of the light does not seem to have an effect. I've just eliminated this parameter and the result is still the same. – mipa Jul 01 '15 at 14:29
  • Cool, good to hear that it (in parts) helped you. I will update my answer so it fits your analyses. My first attempt was to switch off the light or set it to a transparent light, but that was a mistake. On my windows pc with amd hd 4200 it makes no big difference. – aw-think Jul 01 '15 at 14:38