Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/383.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 虚拟轨迹球的实现_Java_Math_Opengl_Quaternions - Fatal编程技术网

Java 虚拟轨迹球的实现

Java 虚拟轨迹球的实现,java,math,opengl,quaternions,Java,Math,Opengl,Quaternions,在过去的几天里,我一直在尝试为3D图形程序的用户界面制作一个虚拟轨迹球的工作实现。但我有麻烦了 从数字和许多测试来看,问题似乎是四元数的实际串联,但我不知道也不这么认为。我以前从未使用过四元数或虚拟轨迹球,这对我来说是全新的。我使用的是JOGL提供的四元数类。我试着自己做,结果成功了(或者至少据我所知),但结果一团糟,所以我选择了JOGL's 当我不连接四元数时,我看到的轻微旋转似乎是我想要的,但当它只是在任何方向上移动一点点时,这当然很难。此代码基于OpenGL wiki上的 当我使用Quat

在过去的几天里,我一直在尝试为3D图形程序的用户界面制作一个虚拟轨迹球的工作实现。但我有麻烦了

从数字和许多测试来看,问题似乎是四元数的实际串联,但我不知道也不这么认为。我以前从未使用过四元数或虚拟轨迹球,这对我来说是全新的。我使用的是JOGL提供的
四元数
类。我试着自己做,结果成功了(或者至少据我所知),但结果一团糟,所以我选择了JOGL's

当我不连接四元数时,我看到的轻微旋转似乎是我想要的,但当它只是在任何方向上移动一点点时,这当然很难。此代码基于OpenGL wiki上的

当我使用
Quaternion
类的
mult(Quaternion q)
方法时,图形几乎不会移动(甚至比不尝试连接四元数还要小)

当我尝试四元数
类的
add(quaternionq)`方法来取乐时,我得到的东西至少可以旋转图形,但不会以任何连贯的方式旋转。当我移动鼠标时,它会突然弹出并随机旋转。有时,我会得到四元数完全充满了NaN

在我的代码中,我不会显示这两个,我不知道如何处理我的四元数。我知道我想把它们相乘,因为据我所知,这就是它们的连接方式。但正如我所说,我没有成功,我假设在我的代码中的其他地方会出问题

无论如何,我的设置有一个
Trackball
类,带有
public Point3f projectMouse(intx,inty)
方法和
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:


问题是两个四元数的乘法,我可以确认其他所有操作都如预期的那样工作。在乘法过程中,轴发生了异常

两个标准化向量的叉积本身不是标准化的。它的长度是
sin(θ)
。请尝试以下方法:

n = n.normalize().multiply((float) Math.sin(theta / 2.0F));
我做到了

由于C++实现,我能够开发一个工作轨迹球/弧球界面。我的天哪,我仍然不确定问题出在哪里,但我重写了所有内容,甚至编写了自己的

四元数
类,突然整个过程都正常了。我还为向量制作了一个
Vectors
类。我以前有一个
Vector3f
类,但是
Quaternions
Vectors
类充满了静态方法和数组。使四元数上的向量计算变得容易,反之亦然。我将在下面链接这两个类的代码,但这里只显示
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;
    }
}
公共类轨迹球
{
专用静态最终浮动半径=Screen.DFLT\u宽度/2.0F;
私有静态最终整型刷新=50;
私有静态最终浮点SQRT2_=(float)Math.sqrt(2);
专用静态最终浮点SQRT2_u逆=1.0F/SQRT2_u;
私有整数计数;
私有内部objx_ux,objy_ux;
私有浮动[]v1,v2;
私人浮动【】rot【】;
公共轨迹球()
{
v1=新的浮动[4];
v2=新浮点数[4];
rot_u2;=新浮点[]{0,0,0,1};
}
公共无效单击(整数x,整数y)
{
v1=项目(x,y);
}
公共无效拖动(整数x,整数y)
{
v2=项目(x,y);
if(Arrays.equals(v1,v2))返回;
float[]n=Vectors.cross(v2,v1,null);
float[]o=Vectors.sub(v1_,v2_,null);
float dt=向量len(o)/(2.0F*半径1);
dt=dt>1.0F?1.0F:dt<-1.0F?-1.0F:dt;
浮点a=2.0F*(浮点)数学asin(dt);
向量。范数r(n);
向量.mul_r(n,(float)Math.sin(a/2.0F));
if(count_++==REFRESH_417;{count_417;=0;四元数.norm_r(rot_417;}
float[]q=Arrays.copyOf(n,4);
q[3]=(浮点)数学cos(a/2.0F);
rot=四元数.mul(q,rot,rot);
}
公共浮动[]getAxis