Spa*_*ace 2 java opengl math quaternions
在过去的几天里,我一直在尝试为用户界面制作虚拟轨迹球,以实现类似3D图形的程序.但我遇到了麻烦.
看看数字和许多测试,问题似乎是我的四元数的实际连接,但我不知道或不这么认为.我以前从未使用四元数或虚拟轨迹球,这对我来说都是新手.我正在使用Quaternion
JOGL提供的类.我尝试制作自己的并且它起作用(或者至少就我所知道的那样)但是它完全混乱所以我只是选择了JOGL.
当我不连接四元数时,我看到的轻微旋转似乎就是我想要的,但当然只有它向任何方向移动一点时才很难.此代码基于OpenGL wiki上的Trackball Tutorial.
当我使用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;
}
Run Code Online (Sandbox Code Playgroud)
这是旋转方法:
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
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我越走越近,我发现了一些简单的错误.
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;
}
Run Code Online (Sandbox Code Playgroud)
这是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]);
Run Code Online (Sandbox Code Playgroud)
更改名称的原因是因为我删除了Trackball
课程中的所有内容并重新开始.可能不是最好的主意,但是哦.
EDIT2:
我可以非常肯定地说投射到球体上没有任何问题.
我还可以说,就整个事情而言,它似乎是问题的矢量.角度看起来很好,但矢量似乎跳了起来.
EDIT3:
问题是两个四元数的乘法,我可以确认其他一切都按预期工作.在乘法过程中,轴有些不堪重负!
小智 5
问题是两个四元数的乘法,我可以确认其他一切都按预期工作.在乘法过程中,轴有些不堪重负!
你是绝对正确的!! 我刚刚提交了正确的乘法,Jogamp接受了我的更改.他们在mult(四元数)上有不正确的乘法.
我相信如果你得到最新的jogl版本,它会有正确的mult(Quaternion)
我做到了!
感谢这个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;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以看到 C++ 示例有很多相似之处。另外我想指出的是,目前还没有设置objx_
和objy_
值的方法。这些用于设置可以移动的图表的中心。只是说,这样您就不会为这些领域摸不着头脑。