JavaFX相机绕X轴旋转异常问题求助
JavaFX 3D相机旋转异常问题解决
问题现象
尝试让相机绕X轴旋转时,相机未按预期绕自身轴转动,反而以全局原点(0,0,0)为中心旋转。初始状态下相机对准立方体,左移鼠标(X轴角度+2)时立方体沿大圆周移动,右移则反向;但将旋转轴应用到立方体时表现正常。期望实现现实转向逻辑:相机右转时立方体左移,左转时立方体右移。
原问题代码
public class javafx extends Application { int ze = 0; int ye = 0; int xe = 0; PerspectiveCamera cam = new PerspectiveCamera(); //the rotation angles// Rotate rx = new Rotate(); { rx.setAxis(Rotate.X_AXIS); } Rotate ry = new Rotate(); { ry.setAxis(Rotate.Y_AXIS); } Rotate rz = new Rotate(); { rz.setAxis(Rotate.Z_AXIS); } int xt = 0; int yt = 0; int one; int two; boolean flip = false; public static void addRotate(Node node, Point3D rotationAxis, double angle) { ObservableList<Transform> transforms = node.getTransforms(); try { for (Transform t : transforms) { rotationAxis = t.inverseDeltaTransform(rotationAxis); } } catch (NonInvertibleTransformException ex) { throw new IllegalStateException(ex); } transforms.add(new Rotate(angle, rotationAxis)); } public void start(Stage stage) { Box cube = new Box(); cam.getTransforms().addAll(rx, rz, ry); cube.setDepth(100.0); cube.setHeight(100.0); cube.setWidth(200.0); cube.setCullFace(CullFace.BACK); cube.setDrawMode(DrawMode.FILL); PhongMaterial material = new PhongMaterial(); material.setDiffuseColor(Color.BROWN); cube.setMaterial(material); cube.setTranslateX(1500.0); cube.setTranslateY(500.0); cube.setTranslateZ(0.0); cam.setTranslateX(0); cam.setTranslateY(0); cam.setTranslateZ(0); cam.setScaleX(2); cam.setScaleY(2); cam.setScaleZ(2); Group root = new Group(cube, cam); Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); double screenHeight = dimension.getHeight(); double screenWidth = dimension.getWidth(); Scene scene = new Scene(root, 0,0); stage.setFullScreen(true); scene.setCamera(cam); stage.setTitle("3d space"); stage.setScene(scene); stage.show(); stage.setOnHiding( event -> { System.exit(0);} ); //to get the input from a mouse vvvv // ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); Runnable toRun; toRun = new Runnable() { public void run() { if(!flip){ one = MouseInfo.getPointerInfo().getLocation().x; two = MouseInfo.getPointerInfo().getLocation().x; flip = true; }else{ flip = false; if(one > MouseInfo.getPointerInfo().getLocation().x){ xt = xt +2; ry.setAngle(xt); System.out.println("left"); }else{ if(one < MouseInfo.getPointerInfo().getLocation().x){ System.out.println("right"); xt = xt -2; ry.setAngle(xt); } } } } }; ScheduledFuture<?> handle = scheduler.scheduleAtFixedRate(toRun, 1, 1, TimeUnit.MILLISECONDS); } public static void main(String args[]){ launch(args); } }
问题根源
JavaFX中直接给相机添加Rotate变换时,默认基于全局坐标系原点旋转,而非相机自身的局部坐标系。这导致相机绕原点转动,视觉上呈现为立方体在绕原点做圆周运动,而非相机自身转向。
修复方案
关键修改点
- 改用相机局部旋转属性:使用
PerspectiveCamera的rotate和rotationAxis属性实现局部旋转,默认基于相机自身坐标系计算,无需额外设置旋转中心。 - 修正线程安全问题:将
ScheduledExecutorService替换为JavaFX原生的AnimationTimer,确保所有UI操作在FX应用线程执行。 - 匹配现实转向逻辑:反转旋转角度的增减方向,实现“鼠标右移→相机右转→立方体左移”的自然体验。
修正后的完整代码
import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.Box; import javafx.scene.shape.CullFace; import javafx.scene.shape.DrawMode; import javafx.scene.transform.Rotate; import javafx.stage.Stage; import java.awt.MouseInfo; public class JavaFX3DCameraFix extends Application { private PerspectiveCamera cam = new PerspectiveCamera(); private double currentYAngle = 0; private double lastMouseX; private boolean firstMouseMove = true; @Override public void start(Stage stage) { // 创建立方体 Box cube = new Box(200.0, 100.0, 100.0); cube.setCullFace(CullFace.BACK); cube.setDrawMode(DrawMode.FILL); PhongMaterial material = new PhongMaterial(); material.setDiffuseColor(Color.BROWN); cube.setMaterial(material); cube.setTranslateX(1500.0); cube.setTranslateY(500.0); cube.setTranslateZ(0.0); // 配置相机 cam.setTranslateX(0); cam.setTranslateY(0); cam.setTranslateZ(0); cam.setFieldOfView(60); // 设置合理视野角度 Group root = new Group(cube, cam); Scene scene = new Scene(root, 0, 0, true); // 启用3D场景 scene.setCamera(cam); stage.setFullScreen(true); stage.setTitle("3D Space"); stage.setScene(scene); stage.show(); // 使用AnimationTimer处理鼠标输入,确保线程安全 new AnimationTimer() { @Override public void handle(long now) { double mouseX = MouseInfo.getPointerInfo().getLocation().getX(); if (firstMouseMove) { lastMouseX = mouseX; firstMouseMove = false; return; } // 计算鼠标移动增量,调整旋转灵敏度 double deltaX = mouseX - lastMouseX; currentYAngle -= deltaX * 0.1; // 应用相机局部Y轴旋转 cam.setRotationAxis(Rotate.Y_AXIS); cam.setRotate(currentYAngle); lastMouseX = mouseX; } }.start(); } public static void main(String args[]) { launch(args); } }
效果说明
- 相机绕自身Y轴旋转,转动时不会偏离初始观测方向,完全符合现实转向逻辑。
- 鼠标右移时,相机右转,立方体在视野中向左移动;鼠标左移时则相反。
- 使用
AnimationTimer替代线程池,避免了跨线程操作UI的风险,提升了程序稳定性。
内容的提问来源于stack exchange,提问作者king cannibal
相关产品推荐
相关产品推荐

