在过去的几天里,我一直在尝试为类似 3D 图形的程序的用户界面制作一个虚拟轨迹球的工作实现。但我遇到了麻烦。
查看数字和许多测试,问题似乎是我的四元数的实际串联,但我不知道或不这么认为。我以前从未使用过四元数或虚拟轨迹球,这对我来说是全新的。我正在使用Quaternion
JOGL 提供的类。我试着自己做,它奏效了(或者至少据我所知),但它完全是一团糟,所以我只选择了 JOGL 的。
当我不连接四元数时,我看到的轻微旋转似乎是我想要的,但是当它只向任何方向移动一点点时当然很难。此代码基于OpenGL wiki 上的轨迹球教程。
当我使用Quaternion
类的mult (Quaternion q)
方法时,图形几乎不会移动(甚至比不尝试连接四元数还要少)。
当我尝试使用 Quaternion class's
add (Quaternion q)` 方法来获得乐趣时,我得到的东西至少可以旋转图形,但不是以任何连贯的方式。当我移动鼠标时,它会闪烁并随机旋转。有时我会得到完全用 NaN 填充的四元数。
在我的代码中,我不会显示其中任何一个,我不知道如何处理我的四元数。我知道我想将它们相乘,因为据我所知,这就是它们的连接方式。但就像我说的我没有成功,我假设搞砸了在我的代码中的其他地方。
无论如何,我的设置有一个Trackball
带有public Point3f projectMouse (int x, int y)
方法和一个的类,我在public void rotateFor (Point3f p1, Point3f p2)
哪里Point3f
创建了一个类。另一个名为的类Camera
有一个public void transform (GLAutoDrawable g)
方法,该方法将调用 OpenGL 方法以根据轨迹球的四元数进行旋转。
这是代码:
public Point3f projectMouse (int x, int y)
{
int off = Screen.WIDTH / 2; // Half the width of the GLCanvas
x = x - objx_ - off; // obj being the 2D center of the graph
y = off - objy_ - y;
float t = Util.sq(x) + Util.sq(y); // Util is a class I made with
float rsq = Util.sq(off); // simple some math stuff
// off is also the radius of the sphere
float z;
if (t >= rsq)
z = (rsq / 2.0F) / Util.sqrt(t);
else
z = Util.sqrt(rsq - t);
Point3f result = new Point3f (x, y, z);
return result;
}
这是旋转方法:
public void rotateFor (Point3f p1, Point3f p2)
{
// Vector3f is a class I made, I already know it works
// all methods in Vector3f modify the object's numbers
// and return the new modify instance of itself
Vector3f v1 = new Vector3f(p1.x, p1.y, p1.z).normalize();
Vector3f v2 = new Vector3f(p2.x, p2.y, p2.z).normalize();
Vector3f n = v1.copy().cross(v2);
float theta = (float) Math.acos(v1.dot(v2));
float real = (float) Math.cos(theta / 2.0F);
n.multiply((float) Math.sin(theta / 2.0F));
Quaternion q = new Quaternion(real, n.x, n.y, n.z);
rotation = q; // A member that can be accessed by a getter
// Do magic on the quaternion
}
编辑:
我越来越近了,我发现了一些简单的错误。
1:JOGL 实现将 W 视为实数,而不是 X,我使用 X 表示实数
2:我不是从四元数开始的 1 + 0i + 0j + 0k
3:我没有将四元数转换为opengl的轴/角度
4:我没有将角度转换为opengl的度数
同样正如马库斯指出的那样,我没有使正常正常化,当我这样做时,我看不到太多变化,认为很难说,但他是对的。
现在的问题是,当我做整个事情时,图表会以一种你永远不会相信的剧烈震动。它(有点)朝着你想要的方向移动,但是癫痫发作太猛烈了,无法从中做出任何事情。
这是我的新代码,有一些名称更改:
public void rotate (Vector3f v1, Vector3f v2)
{
Vector3f v1p = v1.copy().normalize();
Vector3f v2p = v2.copy().normalize();
Vector3f n = v1p.copy().cross(v2p);
if (n.length() == 0) return; // Sometimes v1p equals v2p
float w = (float) Math.acos(v1p.dot(v2p));
n.normalize().multiply((float) Math.sin(w / 2.0F));
w = (float) Math.cos(w / 2.0F);
Quaternion q = new Quaternion(n.x, n.y, n.z, w);
q.mult(rot);
rot_ = q;
}
这是OpenGL代码:
Vector3f p1 = tb_.project(x1, y1); // projectMouse [changed name]
Vector3f p2 = tb_.project(x2, y2);
tb_.rotate (p1, p2);
float[] q = tb_.getRotation().toAxis(); // Converts to angle/axis
gl.glRotatef((float)Math.toDegrees(q[0]), q[1], q[2], q[3]);
改名的原因是因为我删除了Trackball
课堂上的所有内容并重新开始。可能不是最好的主意,但哦,好吧。
编辑2:
我可以非常肯定地说,投影到球体上没有任何问题。
我也可以说,就整个事情而言,问题似乎是向量。角度看起来很好,但矢量似乎跳来跳去。
编辑3:
问题是两个四元数的乘法,我可以确认其他一切都按预期工作。在乘法过程中,轴出现了问题!