22

解決済み


エンジンで 3D ポータル システムを作成しています (ポータル ゲームなど)。各ポータルには、クォータニオンに保存された独自の向きがあります。ポータルの 1 つで仮想シーンをレンダリングするには、2 つの四元数の差を計算する必要があり、その結果を使用して仮想シーンを回転させます。

左の壁に最初のポータルを作成し、右の壁に 2 つ目のポータルを作成する場合、一方から他方への回転は 1 つの軸でのみ行われますが、たとえば、最初のポータルが床に作成され、2 つ目のポータルが床に作成される場合などです。右の壁では、一方から他方への回転が 2 軸になる可能性があります。これが問題です。回転がうまくいかないからです。

たとえば、X軸と軸の向きが1つのクォータニオンに一緒に格納されており、 * (または* )Zを手動で乗算するために個別に必要があるため、問題が存在すると思いますが、1つのクォータニオンのみでそれを行う方法(差分クォータニオン)? または、シーンを正しく回転させる他の方法はありますか?XZZX

編集:

この写真には 2 つのポータル P1 と P2 があり、矢印はそれらがどのように回転しているかを示しています。P1 を見ていると、何が P2 を見ているかがわかります。この写真の仮想シーンのようにメイン シーンを回転させるために必要な回転を見つけるには、次のようにします。

  1. クォータニオン P2 からクォータニオン P1 への差の取得
  2. 結果を Y 軸で 180 度回転 (ポータルの UP)
  3. 結果を使用して仮想シーンを回転させる

上記の方法は、違いが 1 つの軸だけで発生する場合にのみ機能します。1 つのポータルが床または天井にある場合、差分クォータニオンが複数の軸で構築されているため、これは機能しません。示唆されているように、P1 の四元数を P2 の四元数に乗算しようとしましたが、逆に、これは機能しません。

ここに画像の説明を入力

編集2:

P2 と P1 の違いを見つけるために、次のことを行っています。

Quat q1 = P1->getOrientation();
Quat q2 = P2->getOrientation();

Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //

Quat::diff 関数は次のとおりです。

GE::Quat GE::Quat::diff(const Quat &a, const Quat &b)
{
    Quat inv = a;
    inv.inverse();
    return inv * b;
}

逆:

void GE::Quat::inverse()
{
    Quat q = (*this);
    q.conjugate();
    (*this) = q / Quat::dot((*this), (*this));
}

共役:

void GE::Quat::conjugate()
{
    Quat q;
    q.x = -this->x;
    q.y = -this->y;
    q.z = -this->z;
    q.w = this->w;

    (*this) = q;
}

内積:

float GE::Quat::dot(const Quat &q1, const Quat &q2)
{
    return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
}

オペレーター*:

const GE::Quat GE::Quat::operator* ( const Quat &q) const
{
    Quat qu;
    qu.x = this->w*q.x + this->x*q.w + this->y*q.z - this->z*q.y;
    qu.y = this->w*q.y + this->y*q.w + this->z*q.x - this->x*q.z;
    qu.z = this->w*q.z + this->z*q.w + this->x*q.y - this->y*q.x;
    qu.w = this->w*q.w - this->x*q.x - this->y*q.y - this->z*q.z;
    return qu;
}

オペレーター/:

const GE::Quat GE::Quat::operator/ (float s) const
{
    Quat q = (*this);
    return Quat(q.x / s, q.y / s, q.z / s, q.w / s);
}

GLMライブラリでテストしたので、これらすべてが機能します

4

5 に答える 5

40

diffのような四元数を見つけたい場合diff * q1 == q2は、乗法逆数を使用する必要があります。

diff * q1 = q2  --->  diff = q2 * inverse(q1)

where:  inverse(q1) = conjugate(q1) / abs(q1)

and:  conjugate( quaternion(re, i, j, k) ) = quaternion(re, -i, -j, -k)

クォータニオンが回転クォータニオンである場合、それらはすべて単位クォータニオンである必要があります。これにより、逆数を簡単に見つけることができます。なぜなら、 、、およびコンポーネントを否定するだけabs(q1) = 1で yourを見つけることができるからです。inverse(q1) = conjugate(q1)ijk


ただし、説明するシーンベースの幾何学的構成の種類については、変換を正しく計算する必要があるため、実際には上記を実行したくないでしょう。

すべてを正しく行うための最も簡単な方法は、四元数を 4x4 回転行列に変換し、それらを 4x4 変換行列で適切な順序で乗算することです。これは、ほとんどのコンピュータ グラフィックスの入門テキストで説明されています。

手動でユークリッド変換を構成し、回転を四元数形式に保ちながら、四元数を個別の平行移動ベクトルに段階的に適用することは確かに可能です。ただし、この方法は技術的にわかりにくく、コーディング エラーが発生しやすい傾向があります。4x4 行列形式が従来型である理由は十分にあり、その大きな理由の 1 つは、その方法で正しく処理する方が簡単に見えることです。

于 2014-03-04T08:57:02.663 に答える
5

問題を解決しました。結局のところ、2 つのローテーションの違いは必要ありません。1 回転に 180 度の回転を掛けてから、そのように 2 番目の回転の逆数を掛けます (行列を使用):

Matrix m1 = p1->getOrientation().toMatrix();
Matrix m2 = p2->getOrientation().toMatrix();
Matrix model = m1 * Matrix::rotation(180, Vector3(0,1,0)) * Matrix::inverse(m2);

翻訳は次のように計算されます。

Vector3 position = -p2->getPosition();
position = model * position + p1->getPosition();
model = Matrix::translation(position) * model;
于 2014-03-04T12:53:00.420 に答える
1

クォータニオンは次のように機能します。ローカル参照フレームは、仮想のクォータニオン方向 i、j、k として表されます。例えば、入口ドア1に立って矢印の方向を見ている観察者の場合、方向iは矢印の方向を表し、jは上であり、k=ijは観察者の右側を指している。クォータニオン q1 で表されるグローバル座標では、3D 座標の軸は次のとおりです。

q1*(i,j,k)*q1^-1=q1*(i,j,k)*q1',

ここで、q' は共役であり、単位四元数の場合、共役は逆です。

ここでのタスクは、グローバル座標で表されたローカル フレーム 1 の方向 q*(i,j,k)*q' が、グローバル座標でのフレーム 2 の回転方向と一致するように、単位四元数 q を見つけることです。前方が後方になり、左が右になることを意味するスケッチから、つまり

q1*q*(i,j,k)*q'*q1'=q2*(-i,j,-k)*q2'
                   =q2*j*(i,j,k)*j'*q2'

これは、等式によって容易に達成されます。

q1*q=q2*j or q=q1'*q2*j.

ただし、詳細は異なる場合があります。主に、別の軸が j の代わりに「上」の方向を表している可能性があります。


スケッチのグローバル システムが下からのもので、global-i が垂直方向前方、global-j が上、global-k が右を指す場合、local1-(i,j,k) は global-( -i,j,-k)、与える

q1=j. 

local2-(i,j,k) は global-(-k,j,i) であり、次の式で実現できます。

q2=sqrt(0.5)*(1+j), 

以来

(1+j)*i*(1-j)=i*(1-j)^2=-2*i*j=-2*k and 
(1+j)*k*(1-j)=(1+j)^2*k= 2*j*k= 2*i

これを実装の実際の値と比較すると、軸と四元数の方向の割り当てをどのように変更する必要があるかがわかります。

于 2014-03-03T23:18:50.093 に答える