在过去的几天里,我一直试图为一个3D图形样程序的用户界面制作一个虚拟轨迹球的工作实现。但我有麻烦了。
看看数字和许多测试,问题似乎是我的四元数的实际连接,但我不知道,也不认为如此。我以前从来没有用过四元数或者虚拟履带球,这对我来说都是新的。我正在使用JOGL提供的Quaternion类。我试着做我自己的,它起了作用(至少据我所知),但它是一个完整的混乱,所以我只是去了JOGL的。
当我不连接四元数时,我看到的轻微旋转似乎是我想要的,但当然,当它只向任何方向移动一点点时,就很难了。此代码基于轨迹球教程上的OpenGL wiki。
当我使用Quaternion类的mult (Quaternion q)方法时,图形几乎不动(甚至不尝试连接四元数)。
当我尝试四元数class'sadd (Quaternion )的方法时,为了好玩,我得到了一些东西,至少可以旋转图形,但不能以任何连贯的方式旋转。当我移动鼠标的时候,它会散开旋转。偶尔,我会得到四元数完全充满了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类中的所有内容并重新启动。也许不是个好主意,但是哦,好吧。
EDIT2:
我可以非常肯定地说,投射到球体上没有什么不对的。
我也可以说,就整件事而言,问题就在于向量。角度看上去很好,但矢量似乎在附近跳跃。
EDIT3:
问题是两个四元数的乘法,我可以确认其他的一切都像预期的那样工作。在乘法过程中,有什么东西会与轴一起变古怪!!
发布于 2012-07-04 18:23:55
我做到了!
由于这 C++实现,我开发了一个工作轨迹/弧线球接口。我的天啊,我仍然不知道问题出在哪里,但是我重写了所有东西,甚至编写了我自己的Quaternions类,突然之间,整个过程就开始工作了。我还为向量创建了一个Vectors类。我以前有一个Vector3f类,但是Quaternions和Vectors类都是静态方法,并接受数组。以便于对四元数进行矢量计算,反之亦然。我将链接下面这两个类的代码,但这里只显示Trackball类。
今天早上我很快就上了这两门课,所以如果有数学错误的话,那么,呃,哎呀。我只使用我需要使用的东西,并确保它们是正确的。这些课程如下:
四元数:http://pastebin.com/raxS4Ma9
载体:http://pastebin.com/fU3PKZB9
这是我的Trackball课程:
public class Trackball
{
private static final float RADIUS_ = Screen.DFLT_WIDTH / 2.0F;
private static final int REFRESH_ = 50;
private static final float SQRT2_ = (float) Math.sqrt(2);
private static final float SQRT2_INVERSE_ = 1.0F / SQRT2_;
private int count_;
private int objx_, objy_;
private float[] v1_, v2_;
private float[] rot_;
public Trackball ()
{
v1_ = new float[4];
v2_ = new float[4];
rot_ = new float[] {0, 0, 0, 1};
}
public void click (int x, int y)
{
v1_ = project(x, y);
}
public void drag (int x, int y)
{
v2_ = project(x, y);
if (Arrays.equals(v1_, v2_)) return;
float[] n = Vectors.cross(v2_, v1_, null);
float[] o = Vectors.sub(v1_, v2_, null);
float dt = Vectors.len(o) / (2.0F * RADIUS_);
dt = dt > 1.0F ? 1.0F : dt < -1.0F ? -1.0F : dt;
float a = 2.0F * (float) Math.asin(dt);
Vectors.norm_r(n);
Vectors.mul_r(n, (float) Math.sin(a / 2.0F));
if (count_++ == REFRESH_) { count_ = 0; Quaternions.norm_r(rot_); }
float[] q = Arrays.copyOf(n, 4);
q[3] = (float) Math.cos(a / 2.0F);
rot_ = Quaternions.mul(q, rot_, rot_);
}
public float[] getAxis ()
{
return Quaternions.axis(rot_, null);
}
public float[] project (float x, float y)
{
x = RADIUS_ - objx_ - x;
y = y - objy_ - RADIUS_;
float[] v = new float[] {x, y, 0, 0};
float len = Vectors.len(v);
float tr = RADIUS_ * SQRT2_INVERSE_;
if (len < tr)
v[2] = (float) Math.sqrt(RADIUS_ * RADIUS_ - len * len);
else
v[2] = tr * tr / len;
return v;
}
}您可以从C++示例中看到许多相似之处。另外,我想指出,目前还没有设置objx_和objy_值的方法。这些是用来设置可以移动的图形的中心。我只想说,这样你就不会在那些领域里挠头了。
发布于 2012-11-15 23:09:55
问题是这两个四元数的乘法,我可以确认其他的一切都像预期的那样工作。在乘法过程中,有什么东西会与轴发生错乱!
你绝对是对的!!最近我提交了一个正确的乘法,Jogamp接受了我的修改。他们在骡子(四元数)上有不正确的乘法。
我相信如果你得到最新的慢跑释放,它会有正确的骡子(四元数)
发布于 2012-07-03 18:57:32
两个归一化向量的交叉积本身不是归一化的.它的长度是sin(theta)。试一试:
n = n.normalize().multiply((float) Math.sin(theta / 2.0F));https://stackoverflow.com/questions/11314384
复制相似问题