私はここ数日、3D グラフのようなプログラムのユーザー インターフェイス用に仮想トラックボールを実際に実装しようと試みてきました。しかし、私は問題を抱えています。
数値と多くのテストを見ると、問題は私の四元数の実際の連結であるように見えますが、私は知らないか、そうは思いません。これまでクォータニオンや仮想トラックボールを使用したことはありません。これは私にとってまったく新しいことです。QuaternionJOGLが提供するクラスを使用しています。私は自分で作ってみましたが、うまくいきました(少なくとも私が知る限り)が、完全に混乱していたので、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 つの四元数の乗算です。他のすべてが期待どおりに機能することを確認できます。乗算中に軸がおかしい!