私はここ数日、3D グラフのようなプログラムのユーザー インターフェイス用に仮想トラックボールを実際に実装しようと試みてきました。しかし、私は問題を抱えています。
数値と多くのテストを見ると、問題は私の四元数の実際の連結であるように見えますが、私は知らないか、そうは思いません。これまでクォータニオンや仮想トラックボールを使用したことはありません。これは私にとってまったく新しいことです。Quaternion
JOGLが提供するクラスを使用しています。私は自分で作ってみましたが、うまくいきました(少なくとも私が知る限り)が、完全に混乱していたので、JOGLのものを使いました。
クォータニオンを連結しないと、わずかな回転が必要なように見えますが、もちろん、どの方向にも少しだけ動いているときは難しいです。このコードは、OpenGL wikiのトラックボール チュートリアルに基づいています。
Quaternion
クラスのメソッドを使用するとmult (Quaternion q)
、グラフはほとんど動きません (クォータニオンを連結しようとしないよりも少ない)。
楽しみのためにQuaternion add (Quaternion q)` メソッドを試してみclass's
たところ、少なくともグラフを回転させるものは得られましたが、一貫した方法ではありませんでした。マウスを動かすと、飛び散り、ランダムに回転します。ときどき、完全に NaN で満たされたクォータニオンが得られます。
私のコードでは、これらのいずれも表示しません。四元数をどうするか迷っています。私が知っている限り、それらが連結される方法であるため、それらを乗算したいことはわかっています。しかし、私が成功していないと言ったように、失敗は私のコードのどこかにあると思います。
とにかく、私のセットアップにはTrackball
、public Point3f projectMouse (int x, int y)
メソッドとpublic void rotateFor (Point3f p1, Point3f p2)
、WherePoint3f
は私が作成したクラスを持つクラスがあります。呼び出される別のクラスに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 の角度を度に変換していませんでした
また、Markus が指摘したように、私はノーマルをノーマライズしていませんでした。私がノーマライズしたとき、あまり変化が見られませんでした。見分けるのは難しいと思いましたが、彼は正しいです。
今の問題は、私がすべてを行うと、信じられないほど激しくグラフが揺れることです. それは(ちょっと)あなたが望む方向に動きますが、発作が激しすぎて何もできません.
いくつかの名前を変更した新しいコードを次に示します。
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:
問題は 2 つの四元数の乗算です。他のすべてが期待どおりに機能することを確認できます。乗算中に軸がおかしい!