2012-07-03 146 views
2

過去幾天我一直在嘗試爲3D圖形程序的用戶界面製作虛擬軌跡球的工作實現。但我遇到了麻煩。虛擬軌跡球實施

看着數字和許多測試的問題似乎是我的四元數的實際連接,但我不知道也不這麼認爲。我從來沒有使用四元或虛擬軌跡球,這對我來說都是新的。我使用JOGL提供的Quaternion類。我嘗試着製作自己的作品(或者至少據我所知),但它是一個完整的混亂,所以我只用了JOGL。

當我不連接四元數時,我所看到的輕微旋轉似乎是我想要的,但當然,當它只是向任何方向移動時都很難。此代碼基於OpenGL wiki上的Trackball Tutorial

當我使用Quaternion類的mult (Quaternion q)方法時,圖幾乎不會移動(甚至小於不試圖連接四元數)。

當我嘗試Quaternion class's添加(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課堂上的一切,並開始過度。可能不是最棒的主意,但是哦。

EDIT2:

我可以相當好肯定地說,沒有錯與投影到球體。

我也可以說,就整個事情而言,似乎是問題的VECTOR。角度看起來很好,但矢量似乎跳動。

EDIT3:

的問題是兩個四元數乘法,我可以證實,一切按預期工作。在乘法運算過程中,某些東西會與軸發生w!!

回答

1

我做到了!

感謝this C++實現,我能夠開發一個工作軌跡球/弧球界面。我的天哪,我仍然不確定問題是什麼,但我重寫了所有內容,甚至編寫了我自己的課程,並且突然間整個事情都奏效了。我也做了一個Vectors類的向量。我有一個Vector3f類之前,但QuaternionsVectors類是充滿靜態方法,並採取數組。爲了便於在四元數上進行矢量計算,反之亦然。我將鏈接下面這兩個類的代碼,但只有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_值的方法。這些用於設置可以移動的圖形中心。只是說,所以你不要在這些領域劃傷你的頭。

1

兩個歸一化向量的叉積沒有歸一化。它的長度是sin(theta)。試試這個:

n = n.normalize().multiply((float) Math.sin(theta/2.0F)); 
5

問題是兩個四元數的乘法,我可以確認其他一切按預期工作。在乘法運算過程中,某些東西會與軸發生w!!

你是絕對正確的!我剛剛提交了一個正確的乘法,Jogamp已經接受了我的改變。他們在mult(quaternion)上有不正確的乘法。

我相信如果你得到最新的jogl發佈,它會有正確的mult(四元數)