5

Android用のOpenGL ES 1には、27個の小さな立方体で構成されるルービック立方体があります。特定の小さな立方体が視点の真正面になるような回転が必要です。だから私は2つのベクトルが必要です。1 つは、オブジェクトの原点から特定の立方体までのベクトルです。もう 1 つは、原点から視点に向かうベクトルです。次に、それらの外積から回転軸が得られ、内積から角度が得られます。

(0,0,1) (ワールド座標の原点から視点へのベクトル) をオブジェクト座標に変換します。コードは次のとおりです。

    matrixGrabber.getCurrentModelView(gl);
    temporaryMatrix.set(matrixGrabber.mModelView);

    inputVector[0] = 0f; 
    inputVector[1] = 0f;
    inputVector[2] = 1f;
    inputVector[3] = 1f;
    Matrix.multiplyMV(resultVector, 0, temporaryMatrix.InvertMatrix(), 0, inputVector,0);
    resultVector[0]/=resultVector[3];
    resultVector[1]/=resultVector[3];
    resultVector[2]/=resultVector[3];

    inputVector = ..... // appropriate vector due to user-selection 

    axis = Vector.normalized(Vector.crossProduct(Vector.normalized(inputVector), Vector.normalized(resultVector)));
    degree = (float)Math.toDegrees(Math.acos(Vector.dot(Vector.normalized(inputVector), Vector.normalized(resultVector))));

回転には 2 つのクォータニオンを使用します。ユーザーがアクションを選択するたびに、そのローテーションのいずれかが発生する必要があります。コードは次のとおりです。

    Quaternion currentRotation = new Quaternion();
    Quaternion temporaryRotation = new Quaternion();
    .
    .
    .
     currentRotation = (currentRotation).mulLeft(temporaryRotation.set(axis, degree));
     currentRotation.toMatrix(matrix);
     gl.glMatrixMode(GL10.GL_MODELVIEW);
     gl.glMultMatrixf(matrix, 0);

問題は、最初のローテーションで問題なく機能することです。最初のローテーションが何であれ。うまく機能しますが、次の回転では軸と角度が間違っているようです。

たとえば、座標系が

  • X 右 (1,0,0)
  • Y アップ (0,1,0)
  • Z イン (0,0,1)

次に、最初に X を中心に反時計回り (CCW) に 90 度回転すると、

  • X'-右 (1,0,0)
  • Y'-in (0,0,1)
  • Z'-down (0,-1,0)

Z を中心に 90 度反時計回りに 2 番目の回転を行うと、

  • X'-in (0,1,0)
  • Y'-左 (-1,0,0)
  • Z'-down (0,-1,0)

しかし、私は期待しています

  • X-up (0,1,0)
  • Y イン (0,0,1)
  • Z-right(1,0,0)

問題は、resultVector(原点から視点に向かって使用した2番目のベクトル)が正しく変換されないことだと思います。世界座標をオブジェクト座標に変換する方法を知っている人はいますか? オブジェクトが回転したときにオブジェクト座標を決定する方法を知っている人はいますか?

4

2 に答える 2

5

昨日、Rubic Cube パズルをコーディングすることにしました。過去に試したものはどれも私にとって非常に不快で、ついに自分でコーディングする気分/時間が得られたからです。私はすでにそれを終えたので、ここに私の洞察があります:

  1. ルービック キューブの表現

    四元数がこれに適しているとは思いません。代わりに、私はより快適です:

    そのため、変換行列のリストと、3*3*3=27キューブ全体の回転用の追加のリストができました。開始状態では、すべてのサブ キューブに単位回転部分があり、原点は のすべての組み合わせをカバーして{ -1 , 0 ,+1 }ルービック キューブ全体を埋めるように設定されています (各サブ キューブ メッシュのサイズは で1.0あり、 を中心にしています(0,0,0)) 。

    軸

    私のキューブは C++ コードで次のように定義されています。

    reper cube[27]; // reper is transform matrix
    
  2. GUI

    制御と表示をできる限り本物に近づけたかったのです。そのため、回転はターゲット サブキューブ (area0またはarea1) をクリックするだけでマウスで制御され、マウス ドラッグの方向から回転する軸と方向が決定されます。

    開始位置から問題はありません (コードでも問題なく動作するため)。ローカル座標系が既に変更されているため、問題は次の回転で始まります (特に回転軸を変更する場合)。これはすべて台無しになるため、グローバルビューの回転についても同じことが言えます。

  3. ローカル座標系の変更を修正する方法は?

    最初に各座標系の軸を一致させるあいまいな解決策を考え出しました。どの軸がどの軸であるかを検出するには、クエリされた方向と変換マトリックスのすべての軸のドット積を実行し、絶対ドット積が最も高いものを選択します。記号は、座標系が反対であるかどうかを示しているだけです (回転を逆にする必要があることを意味します)。

    C++およびOpenGLスタイルの行列では、次のようになります。

    void RubiCube::axises_unit(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,1.0,0.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,0.0,1.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,0.0,0.0,1.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    

    reper直接および逆変換行列を含むクラスはどこにありますか。get_axis直接行列の内部を覗いて、選択した軸方向の単位ベクトルを返します。はvector_mul内積であり、vector_ld3D ベクトルにx,y,z座標を入力するだけです。

    ユニットマトリックスに軸が揃っていないグローバルキューブマトリックスも取得したため(ビューが上の画像のように回転するため)、特別なベクトル(初期ビューマトリックス値)に対してこの軸マッチングを行う必要がありますこれです:

    void RubiCube::axises_obj(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,+0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,-0.000,-0.906,+0.423); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,-0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    

    x,y,zどちらの関数も、単位変換行列と比較して、どの軸がどの軸で、方向が反対 (sx、sy、sz) であるかを返します。

  4. スライスの回転

    これがパズルの核心です。軸を中心にスライスを簡単に回転させます。これはアニメートに使用されるため、角度のステップは小さくなります (私は 9 度を使用します) が、回転全体は合計で 90 度でなければなりません。そうしないと、ルービック キューブが壊れてしまいます。

    void RubiCube::cube_rotate(int axis,int slice,double ang)
        {
        int j,k,a[3],s[3];
        double p[3],p0[3]={0.0,0.0,0.0},lang;
        reper *r;
        _redraw=true;
        for (k=0;k<27;k++)
            {
            r=&cube[k];
            // local axis,sign
            axises_unit(*r,a[0],a[1],a[2],s[0],s[1],s[2]);
            // lang is local signed angle change
            lang=ang; if (s[axis]<0) lang=-lang;
            // select slice
            r->gpos_get(p);
            j=round(p[axis]+1.0);
            if (j!=slice) continue;
            // rotate global position
            if (axis==0) vector_rotx(p0,p,+ang);
            if (axis==1) vector_roty(p0,p,-ang);
            if (axis==2) vector_rotz(p0,p,+ang);
            r->gpos_set(p);
            // rotate local cube orientation
            if (a[axis]==0) r->lrotx(-lang);
            if (a[axis]==1) r->lroty(-lang);
            if (a[axis]==2) r->lrotz(-lang);
            }
        }
    

    Wherereper::gpos_getは行列の原点を 3D ベクトル (点) として返し、reper::gpos_set基本的に新しい行列の位置を設定します。は、角度でvector_rotx(p0,p,a)ベクトルと軸をp中心に回転します。記号は、クラスからのローテーションに一致するだけです(どこかで違いがありました)。詳細については、最初のリンクを参照してください。p0xa+/-reperreper::lrotxreperx

    ご覧のとおり、スライス キューブを選択するために、各マトリックスの原点座標をトポロジとして直接使用しています。

ここで私のデモを試すことができます: Win32+OpenGL Rubic Cube Demo

そして、ここにいくつかのターンのアニメーションGIFがあります:

アニメーション

[Edit1] RubiCube に簡単なソルバーを追加しました

ソルバーを実装するために、 RubiCubeの内部表現から計算された表面平面カラー マップ (左側 ... 中央の四角形は、使用する面の名前とインデックス) を追加しました。また、ソルバー用の内部コマンド que を追加します (右側の軸と方向):

軸

各コマンドは、2 つの文字列で表されます。

edge slice  CW: R L U D F B
edge slice CCW: R'L'U'D'F'B'
mid  slice  CW: R0L0U0D0F0B0
whole cube  CW: RcLcUcDcFcBc

マップは次のようになります。

int map[6][3][3];

辺、行、列map[side][u][v]の正方形の色が含まれています。簡単な7 ステップのソリューションを実装しました (人間が実際の立方体を解くなど):suv

ソリューションの手順

  1. 入力状態 (ステップではありません)
  2. 白い十字に黄色の中央(黄色の中央が正面を向いています)
  3. 白十字(白い真ん中が正面)
  4. 白い角(白い面が下向き)
  5. 中間層 (最初の 3 つのコマンドを使用)
  6. 最上層の黄色の十字 (4 番目のコマンドを使用)
  7. クロスを並べ替えて側面を合わせる (5 番目のコマンド) およびコーナーを並べ替える (6 番目のコマンド)
  8. 立方体を完成させるための最上層の角の向き (7 番目のコマンド)

ソルバーは単純で、文字列 (最適化されていない) で動作するため、少し遅くなりますが、私のセットアップでは完全なソリューションに最大 50 ミリ秒しかかかりません。ここでアップグレードされたデモを試すことができます:

解決中に、まだ定義されていないケースがいくつかある場合があります (バグまたはコード内のケースの欠落による)。このような場合、アプリは粗雑にハングアップします (まだウォッチドッグを実装していません)。キーは、同梱のテキスト ファイルに記述されています。

私はソルバーを軽量 (cca 300 行のコード) にしたので、見つかったソリューションは最適とはほど遠いものです。たとえば、4 つのコーナーをテストする代わりに、1 つのみをテストし、キューブをループで回転させて、不要なターンを引き起こします。それらのいくつかは後で除外されますが、平均的な人間 (私の) ソリューションは最大 200 ターンであり、このソルバーは代わりに最大 300 ターンを返します (最悪の場合、今まで見つけた)。

于 2016-08-18T17:17:14.283 に答える
1

何が起こるかというと、この変換をモデル (あなたの場合は回転) に適用すると、It の基本ベクトルも回転します。座標系も回転させるか、モデルの一人称視点から見ているかのように考えてください。あなたが行うすべての変換は、次の変換に影響します。

通常は独自の座標系を保持したいので、キューブを回転させるのではなく、カメラをキューブの周りに移動することを検討してください。API または Web で「lookAt」メソッドを見つけることができると確信しています。cameraPosition、lookAtPoint、upVector の 3 つのベクトルを使用する必要があります。このアプローチでは、キューブを「lookAtPoint」でもある (0,0,0) に配置できます。最初の cameraPosition は (0,0,-1) のようになり、最初の upVector は (0,1,0) になります。次に移動について (おそらく、入力として左右と上下のみを使用します): 上下に移動するには (X を中心とした回転)、次に次の操作を行います。

originalDistance = (cameraPosition-objectPosition).lenght
leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition
camearPosition = cameraPosition + upVector*inputScalar //inputScalar should be a small floating value
cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object
upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition

左/右 (X を中心とした回転) に移動するには、次に次の操作を行います。

originalDistance = (cameraPosition-objectPosition).lenght
leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition
camearPosition = cameraPosition + leftVector*inputScalar //inputScalar should be a small floating value
cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object
leftVector = normalizedVector(crossProduct(cameraPosition, upVector))//generaly cameraPosition-objectPosition
upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition

これは一般的に問題を解決するはずです..(これを一生懸命書いてい​​るので、間違いを犯した場合は教えてください)

オブジェクト自体を回転させるアプローチについては、オブジェクト自体の座標系での四元数を見つけて、その座標系を中心に回転させる必要があります。また、数学のスキルがあれば非常に簡単です。それ以外の場合は、2 つの角度 (X,Y) を定義し、入力を介して直接変更し、(1,0,0,X) と (0,1,0,Y) のクォータニオンを使用することもできますが、問題が発生する可能性があります。 Yが90度のときのこのアプローチ..

これが役立つことを願っています。

于 2012-07-13T12:36:57.420 に答える