JDK-8169053 : 3D shapes are not Z-ordered correctly and move relatively to each other when the nearClip value of a PerspectiveCamera is very small
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u102
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: other
  • CPU: x86
  • Submitted: 2016-10-29
  • Updated: 2016-11-03
  • Resolved: 2016-11-03
Description
FULL PRODUCT VERSION :
java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.14393]

A DESCRIPTION OF THE PROBLEM :
Problem was raised here: http://stackoverflow.com/questions/40317114/how-to-make-3d-shapes-appear-correctly-with-a-camera

Setting a very small value for a PerspectiveCamera's nearClip causes problems such as wrong Z-ordering, wrong position calculation and visual artifacts. The javadoc specifies "nearClip is specified as a value greater than zero. A value less than or equal to zero is treated as a very small positive number."

In the given code the following happens for the different nearClip values (samples):

* Default (0.1): no issues
* 0.001: the contact area between the red box and the cylinder renders incorrectly.
* 0.0001: as above + Z-ordering issues when zooming in and out (with mousewheel) + red box deforms.
* 0: no artifacts or rendering issues. Z-ordering is wrong. Panning the camera (mouse drag) causes the cylinder to move with respect to the red box.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Set the nearClip in the given code to the various values mentioned in the description.
2. Observe the 3D shapes' behavior. Zoom in and out using mouse scroll and pan using mouse drag.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
None of the values should cause the mentioned problems. If there is a minimum value for nearClip either all values less than it should default to that value, or an exception is thrown. The javadoc seems to support the first option.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class MyApp extends Application {

	DoubleProperty transX = new SimpleDoubleProperty();
	DoubleProperty transY = new SimpleDoubleProperty();
	DoubleProperty transZ = new SimpleDoubleProperty();
	
	double startX, startY, curX, curY;

	@Override
	public void start(Stage stage) throws Exception {

		Box box1 = new Box(30, 30, 5);
		box1.setMaterial(new PhongMaterial(Color.RED));
		Box box2 = new Box(30, 30, 5);
		box2.setMaterial(new PhongMaterial(Color.GREEN));
		box2.setTranslateX(32);
		Cylinder cyn = new Cylinder(5, 15);

		Group root = new Group(box1, box2, cyn);
		root.getTransforms().addAll(new Translate(0, 0, 200),
									new Rotate(-60, Rotate.X_AXIS),
		                            new Rotate(-45, Rotate.Z_AXIS));

		SubScene subs = new SubScene(root, 0, 0, true, SceneAntialiasing.BALANCED);

		PerspectiveCamera camera = new PerspectiveCamera(true);
		camera.setFarClip(1000);
		camera.setNearClip(0.1); // set to smaller values
		camera.translateZProperty().bind(transZ);
		camera.translateXProperty().bind(transX);
		camera.translateYProperty().bind(transY);
		subs.setCamera(camera);

		Pane pane = new Pane(subs);
		subs.widthProperty().bind(pane.widthProperty());
		subs.heightProperty().bind(pane.heightProperty());

		pane.setOnScroll(e -> transZ.set(transZ.get() + e.getDeltaY() * 0.2));
		pane.setOnMousePressed(e -> {
			startX = curX = e.getX();
			startY = curY = e.getY();
		});
		pane.setOnMouseDragged(e -> {
			startX = curX;
			startY = curY;
			curX = e.getX();
			curY = e.getY();
			double deltaX = curX - startX;
			double deltaY = curY - startY;
			double deltaZ = deltaY * Math.sqrt(3);
			transX.set(transX.get() - deltaX);
			transY.set(transY.get() - deltaY);
			transZ.set(transZ.get() + deltaZ);
		});
		
		Scene scene = new Scene(pane, 500, 500);
		stage.setScene(scene);
		stage.sizeToScene();
		stage.show();
	}

	public static void main(String[] args) throws Exception {

		launch(args);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not set nearClip to less than 0.1.


Comments
The Z buffer has finite precision and it is affected by the ratio of farClip to nearClip. To achieve the best result, application should try to push the nearClip plane out and pull the farClip plane in as much as possible.
03-11-2016