我正在尝试开发一种Java3D方法,用于从当前查看方向到对象中心的方向递增旋转宇宙。
换句话说,我希望3D Universe旋转100个短步,以便我单击的对象似乎逐渐移动到屏幕的中心。
我已经在StackOverflow(以及在Web上)上审查了有关3D旋转问题的各种答案,但是几乎所有这些答案都是特定于旋转对象的,而不是世界本身。
我也尝试过检查线性代数,但这并不能帮助我确定满足我要求的特定于Java的函数。
到目前为止,我已经尝试定义一组增量XYZ坐标,并在每次循环遍历中动态使用lookAt()。这几乎可行,但是我看不到任何方法来保留或获取从一个完整的旋转传递到下一个完整的旋转的视点值的方法。每次旋转通过都会从原点开始。
我还尝试通过获取目标转换与开始转换之间的差异并除以增量数量(并移除缩放值),然后在每次通过时将增量旋转矩阵添加到当前视图方向来定义旋转矩阵。循环。对于增量值为1而言,它工作得很好。但是将旋转分为两个或多个增量总是会产生“ BadTransformException:ViewPlatform上方的非全等变换”错误。 (我已经阅读了Java3D API参考中有关此异常的微不足道的文档;由于我可以从中得到的所有信息,它也可能是用Urdu编写的。似乎没有像3D上下文术语这样的简单英语定义。 Google可以看到的任何地方的“仿射”或“剪切”或“一致”或“均匀”。)
然后,我尝试捏合代码以提供AxisAngle4d,获取角度(以弧度为单位),将该角度划分为所需的增量,然后旋转增量角度值。没错,这旋转了整个世界,但是离我选择的物体不远,并且没有任何我能看到的图案。
无奈之下,我尝试在提取的角度上使用rotX和rotY(将Z设置为端点),甚至在其中盲目地抛出了两个Math.cos()和Math.sin()包装器。仍然没有喜悦。
我的直觉告诉我,我已经掌握了基础知识,并且Java3D中有一个相对简单的解决方案。但是很明显,我正在遇到一堵理解墙。与其继续讨论,不如继续研究一下,看看这里是否有人可以提出Java3D解决方案。首选代码,但我愿意尝试对线性代数进行解释,如果这能使我获得代码解决方案的话。
以下是我使用Java的Timer方法来安排旋转增量的方法的核心。我需要帮助的部分就在ActionListener之前。大概就是魔术代码所到之处,它会创建某种增量旋转值,我可以(在循环中)将其应用于当前视图方向,以便旋转宇宙而不会出现“非一致”错误。
private void flyRotate(double endX, double endY, double endZ)
{
// Rotate universe by increments until target object is centered in view
//
// REQUIREMENTS
// 1. Rotate the universe by NUMROTS increments from an arbitrary (non-origin)
// 3D position and starting viewpoint to an ending viewpoint using the
// shortest path and preserving the currently defined "up" vector.
// 2. Use the Java Timer() method to schedule the visual update for each
// incremental rotation.
//
// GLOBALS
// rotLoop contains the integer loop counter for rotations (init'd to 0)
// viewTransform3D contains rotation/translation for current viewpoint
// t3d is a reusable Transform3D variable
// vtg contains the view platform transform group
// NUMROTS contains the number of incremental rotations to perform
//
// INPUTS
// endX, endY, endZ contain the 3D position of the target object
//
// NOTE: Java3D v1.5.1 or later is required for the Vector3D getX(),
// getY(), and getZ() methods to work.
final int delay = 20; // milliseconds between firings
final int pause = 10; // milliseconds before starting
// Get translation components of starting viewpoint vector
Vector3d viewVector = new Vector3d();
viewTransform3D.get(viewVector);
final double startX = viewVector.getX();
final double startY = viewVector.getY();
final double startZ = viewVector.getZ();
// Don't try to rotate to the location of the current viewpoint
if (startX != endX || startY != endY || startZ != endZ)
{
// Get a copy of the starting view transform
t3d = new Transform3D(viewTransform3D);
// Define the initial eye/camera position and the "up" vector
// Note: "up = +Y" is just the initial naive implementation
Point3d eyePoint = new Point3d(startX,startY,startZ);
Vector3d upVector = new Vector3d(0.0,1.0,0.0);
// Get target view transform
// (Presumably something like this is necessary to get a transform
// containing the ending rotation values.)
Transform3D tNew = new Transform3D();
Point3d viewPointTarg = new Point3d(endX,endY,endZ);
tNew.lookAt(eyePoint,viewPointTarg,upVector);
tNew.invert();
// Get a copy of the target view transform usable by the Listener
final Transform3D tRot = new Transform3D(tNew);
//
// (obtain either incremental rotation angle
// or congruent rotation transform here)
//
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
if (++rotLoop <= NUMROTS)
{
// Apply incremental angle or rotation transform to the
// current view
t3d = magic(tRot);
// Communicate the rotation to the view platform transform group
vtg.setTransform(t3d);
}
else
{
timerRot.stop();
rotLoop = 0;
viewTransform3D = t3d;
}
}
};
// Set timer for rotation steps
timerRot = new javax.swing.Timer(delay,taskPerformer);
timerRot.setInitialDelay(pause);
timerRot.start();
}
}
就像这些事情经常发生的那样,通过退一步并重新思考问题,可能会有更好的方法来完成我要完成的任务。我也乐于接受建设性的建议。
非常感谢您的协助!
更新
让我尝试更具体地定义目标。
我有一个Java3D Universe,其中包含许多Sphere对象。我可以单击每个对象并动态获取其预定义的XYZ坐标。
无论何时,我都在查看所有当前可见的对象,这些对象在特定的XYZ位置和查看方向带有“摄像机”,这些对象包含在保存旋转矩阵和平移矢量的变换中。
(注意:我既可以旋转宇宙,也可以使用鼠标独立于单击对象而平移宇宙。因此,有时包含相机当前旋转矩阵和平移矢量的视图变换不会指向具有已知XYZ的任何目标物体坐标。)
给定摄影机变换和对象的XYZ坐标,我想围绕当前摄影机位置旋转Universe,直到所选对象在屏幕中居中。我想通过一系列离散的增量旋转来做到这一点,每个增量旋转都会被渲染,以使可见的宇宙看起来在查看窗口中“旋转”,直到所选对象居中。 (我正在跟这个对象进行翻译;至少这部分正在工作!)
示例:假设摄影机位于原点,沿Y轴的“向上”为1.0,并且所选对象直接在我的左边居中10个单位。假设我有一个180度的视野,我可以单击在屏幕左侧一直到屏幕顶部和底部之间的中间可见的球体的一半。
当我说出这个词时,宇宙中的每个可见对象都应该看起来像是从我的左边向右边以均匀间隔的步长(假设为50)移动,直到选定的对象恰好在屏幕上居中。
用编码的术语来说,我需要弄清楚Java3D代码,通过它我可以绕虚构的线旋转宇宙,该虚构的线穿过我的相机位置(当前为0,0,0),并且与假想线的Y轴完美对齐宇宙的坐标系。 (即,旋转轴扫过一个平面,其中Z始终等于相机位置的Z分量。)
复杂的要求是:
可以在原点以外的3D空间中平移相机。
相对于相机的当前位置和视图,对象可以位于3D空间中的任何位置,包括可见但完全不在屏幕上(视锥范围之外)。
旋转应该走最短的路径-一次旋转的宇宙不要超过180度。
作为旋转过程的第一步,不应该对可见的宇宙进行任何“跳跃”或“扭曲”。即,应保留当前的“向上”向量(而不是宇宙的绝对“向上”向量)。
因此存在一个问题:给定一个保存(虚拟)摄影机当前平移和旋转信息的变换,以及目标对象的Universe空间中的XYZ坐标,什么Java3D代码将以N个相等的步长围绕摄影机旋转Universe,直到该对象成为居中于屏幕?
大概该解决方案分为两个部分:首先,一些3D数学(用Java3D表达)仅在给定摄影机变换和对象的XYZ坐标的情况下计算增量旋转信息;第二个是循环,该循环[将增量旋转应用于当前的观看变换并更新屏幕],直到循环计数器等于增量数为止。
正是3D数学部分击败了我。我没有看到,也无法解决从当前摄影机变换和目标对象位置获取某种形式的增量旋转信息的方法,然后将其应用于摄影机变换。至少,我还没有发现任何不会引起跳跃或扭曲或不相等的增量移动步长的方法(或“ ViewPlatform上方的非一致变换”异常)。
必须有一个简单的解决方案。
最佳答案
因此,如果我正确理解,您的目标是旋转相机,使其以所选对象为中心,但是旋转不应围绕任意向量,而应保留相机的“向上”方向。
然后可能有效的解决方案:
首先,计算必要的“向上”矢量的旋转角度(我们称其为A),以使相机面向所需的对象。
其次,计算沿“向上”向量所必需的平移距离/方向(我们称其为D),以使对象根据需要与摄像机对齐。这可能只是相机/对象之间的Z / Y坐标之差。
通过将A / D除以N来找到dA和dD,N / D是您想要使运动平滑所需的增量数。
在定时器/时间循环中,将A / D分别以dA / dD递增N次,使其达到最终值。请记住,您正在围绕摄像机的“上”矢量和当前位置而不是原点旋转摄像机。
如果您想要更平滑,更逼真的外观旋转,请考虑使用SLERP。
关于java - Java3D:逐步旋转Universe,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10628869/