首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >虚拟轨迹球实现

虚拟轨迹球实现
EN

Stack Overflow用户
提问于 2012-07-03 15:35:38
回答 3查看 3.7K关注 0票数 5

在过去的几天里,我一直试图为一个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方法来旋转。

下面是代码:

代码语言:javascript
复制
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;
}

以下是旋转的方法:

代码语言:javascript
复制
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,我没有把角度转换成度。

同样,正如马库斯指出的那样,我并没有将正常状态正常化,当我这样做的时候,我看不到太多的变化,我觉得很难判断,他是对的。

现在的问题是,当我完成整件事情时,图表会像你永远不会相信的那样剧烈地抖动。它(有点)朝你想要的方向移动,但是癫痫发作太厉害了,无法用它制造任何东西。

下面是我的新代码,有几处更改了名称:

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
    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:

问题是两个四元数的乘法,我可以确认其他的一切都像预期的那样工作。在乘法过程中,有什么东西会与轴一起变古怪!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-04 18:23:55

我做到了!

由于 C++实现,我开发了一个工作轨迹/弧线球接口。我的天啊,我仍然不知道问题出在哪里,但是我重写了所有东西,甚至编写了我自己的Quaternions类,突然之间,整个过程就开始工作了。我还为向量创建了一个Vectors类。我以前有一个Vector3f类,但是QuaternionsVectors类都是静态方法,并接受数组。以便于对四元数进行矢量计算,反之亦然。我将链接下面这两个类的代码,但这里只显示Trackball类。

今天早上我很快就上了这两门课,所以如果有数学错误的话,那么,呃,哎呀。我只使用我需要使用的东西,并确保它们是正确的。这些课程如下:

四元数:http://pastebin.com/raxS4Ma9

载体:http://pastebin.com/fU3PKZB9

这是我的Trackball课程:

代码语言:javascript
复制
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_值的方法。这些是用来设置可以移动的图形的中心。我只想说,这样你就不会在那些领域里挠头了。

票数 3
EN

Stack Overflow用户

发布于 2012-11-15 23:09:55

问题是这两个四元数的乘法,我可以确认其他的一切都像预期的那样工作。在乘法过程中,有什么东西会与轴发生错乱!

你绝对是对的!!最近我提交了一个正确的乘法,Jogamp接受了我的修改。他们在骡子(四元数)上有不正确的乘法。

我相信如果你得到最新的慢跑释放,它会有正确的骡子(四元数)

票数 5
EN

Stack Overflow用户

发布于 2012-07-03 18:57:32

两个归一化向量的交叉积本身不是归一化的.它的长度是sin(theta)。试一试:

代码语言:javascript
复制
n = n.normalize().multiply((float) Math.sin(theta / 2.0F));
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11314384

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档