JavaFX添加ToolBar后3D内容显示异常问题求助
I’ve run into this exact problem before—adding a ToolBar (or any standard JavaFX control) can throw off 3D rendering because it switches the scene to a mixed 2D/3D rendering pipeline, which messes with depth testing and mesh drawing. Let’s break down what’s happening and how to fix it.
The Problem
Without the ToolBar, your 3D mesh renders perfectly:
But adding even a basic ToolBar breaks the rendering, leading to distorted or incorrectly displayed 3D content:
Why This Happens
JavaFX uses different rendering paths for pure 3D scenes vs. mixed 2D/3D scenes. When you add a ToolBar, it triggers the mixed path, which doesn’t handle raw 3D content in a regular StackPane properly. The depth buffer and rendering order get messed up, causing your mesh to display incorrectly.
The Solution: Isolate 3D Content in a SubScene
The best fix is to put all your 3D content inside a SubScene. This gives the 3D elements their own dedicated rendering context, separate from the 2D UI controls. Here’s how to adjust your code:
Step-by-Step Code Adjustments
- Create a SubScene for 3D: Wrap your 3D group (cube + mesh) in a
SubScenewith 3D enabled. - Use a VBox for Layout: Arrange the ToolBar and SubScene vertically (this makes more sense for a toolbar + content setup than a StackPane).
- Bind Interactions to the SubScene: Ensure mouse drag and zoom logic applies to the 3D area, not the entire scene.
Here’s the full corrected code:
import java.util.*; import javafx.application.Application; import javafx.scene.DepthTest; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.SceneAntialiasing; import javafx.scene.SubScene; import javafx.scene.control.ToolBar; import javafx.scene.input.ScrollEvent; 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 org.apache.commons.math3.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction; public class Main extends Application { double[] xMetricData = {1 * 1000, 10 * 1000, 20 * 1000, 30 * 1000, 40 * 1000}; double[] yMetricData = {1 * 1000, 10 * 1000, 20 * 1000, 30 * 1000, 40 * 1000, 50 * 1000}; double[][] fMetricData = {{25, 5, 10, 15, 30, 30}, {15, -5, 5, 15, 25, 25}, {35, 5, 15, 10, 15, 15}, {15, 25, 15, 25, 35, 35}, {20, 30, 25, 30, 40, 40}}; int size = 50; int cubeSize = 100; // Mouse interaction variables private double mousePosX, mousePosY; private double mouseOldX, mouseOldY; private final Rotate rotateX = new Rotate(20, Rotate.X_AXIS); private final Rotate rotateY = new Rotate(-45, Rotate.Y_AXIS); @Override public void start(Stage primaryStage) { // Create cube for 3D content Group cube = createCube(cubeSize); cube.getTransforms().addAll(rotateX, rotateY); // Create SubScene for 3D rendering SubScene subScene = new SubScene(cube, 800, 600, true, SceneAntialiasing.BALANCED); subScene.setCamera(new PerspectiveCamera()); subScene.setFill(Color.LIGHTGRAY); // Build the 3D mesh TriangleMesh mesh = new TriangleMesh(); int xLength = 0, zLength = 0; for (int x = (int)xMetricData[0]; x < xMetricData[xMetricData.length - 1]; x += 1000) { zLength = 0; for (int z = (int) yMetricData[0]; z < yMetricData[yMetricData.length - 1]; z += 1000) { float y = (float) apacheInterpolation(x, z); mesh.getPoints().addAll(x / 1000.0f, y, z / 1000.0f); zLength++; } xLength++; } // Add texture coordinates for (float x = 0; x < xLength - 1; x++) { for (float y = 0; y < zLength - 1; y++) { float x0 = x / xLength; float y0 = y / zLength; float x1 = (x + 1) / xLength; float y1 = (y + 1) / zLength; mesh.getTexCoords().addAll( x0, y0, // Top-left x0, y1, // Bottom-left x1, y1, // Top-right x1, y1 // Bottom-right ); } } // Add mesh faces for (int x = 0; x < xLength - 1; x++) { for (int z = 0; z < zLength - 1; z++) { int tl = x * zLength + z; int bl = x * zLength + z + 1; int tr = (x + 1) * zLength + z; int br = (x + 1) * zLength + z + 1; int offset = (x * (zLength - 1) + z ) * 8 / 2; mesh.getFaces().addAll(bl, offset + 1, tl, offset + 0, tr, offset + 2); mesh.getFaces().addAll(tr, offset + 2, br, offset + 3, bl, offset + 1); } } // Configure mesh material and view PhongMaterial material = new PhongMaterial(); material.setSpecularColor(Color.BLACK); MeshView meshView = new MeshView(mesh); meshView.setMaterial(material); meshView.setCullFace(CullFace.BACK); meshView.setDrawMode(DrawMode.FILL); meshView.setDepthTest(DepthTest.ENABLE); cube.getChildren().add(meshView); // Setup UI components ToolBar toolBar = new ToolBar(); VBox root = new VBox(toolBar, subScene); // Vertical layout for toolbar + 3D content // Main scene setup Scene scene = new Scene(root, 800, 700); scene.setOnMousePressed(me -> { mouseOldX = me.getSceneX(); mouseOldY = me.getSceneY(); }); scene.setOnMouseDragged(me -> { mousePosX = me.getSceneX(); mousePosY = me.getSceneY(); rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY)); rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX)); mouseOldX = mousePosX; mouseOldY = mousePosY; }); makeZoomable(subScene); primaryStage.setResizable(true); primaryStage.setScene(scene); primaryStage.show(); } // Helper: Interpolation logic private double apacheInterpolation(int x, int z) { PiecewiseBicubicSplineInterpolatingFunction spline = new PiecewiseBicubicSplineInterpolatingFunction(xMetricData, yMetricData, fMetricData); return spline.value(x, z); } // Helper: Create cube (adjust this to match your original implementation) private Group createCube(int cubeSize) { Group cube = new Group(); // Add your cube faces/axes here if needed return cube; } // Helper: Zoom functionality for 3D scene private void makeZoomable(SubScene subScene) { subScene.setOnScroll((ScrollEvent event) -> { double zoomFactor = event.getDeltaY() > 0 ? 1.05 : 0.95; subScene.setScaleX(subScene.getScaleX() * zoomFactor); subScene.setScaleY(subScene.getScaleY() * zoomFactor); subScene.setScaleZ(subScene.getScaleZ() * zoomFactor); event.consume(); }); } public static void main(String[] args) { launch(args); } }
Key Fixes Explained
- SubScene Isolation: The
SubScenemaintains a pure 3D rendering context, so the ToolBar’s 2D rendering doesn’t interfere with depth testing or mesh display. - VBox Layout: Ensures the ToolBar stays at the top and the 3D content fills the rest of the window, avoiding layout conflicts.
- Targeted Interactions: Zoom is now applied directly to the SubScene, so zooming only affects the 3D content (not the entire UI).
This should resolve the rendering issue while keeping your ToolBar and 3D functionality intact.
内容的提问来源于stack exchange,提问作者James

