web-dev-qa-db-ja.com

基本座標の軸ではなく、独自の軸を中心にオブジェクトを回転させることはできますか?

GoogleのOpenGL esの回転の例に従って、Androidアプリ、たとえば次のコードで、(立方体ではなく)単純な正方形を回転させます。

gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z

1つの軸を中心に回転するだけであれば問題なく機能します。

しかし、ある軸を中心に回転し、その後別の軸を中心に回転すると、回転は不公平になります。つまり、回転はベース(グローバル)座標系の軸を中心に行われ、正方形自体の座標系では行われません。

Shahbazのコードで編集

public void onDrawFrame(GL10 gl) {
    //Limpiamos pantalla y Depth Buffer
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);    
    gl.glLoadIdentity();
    //Dibujado
    gl.glTranslatef(0.0f, 0.0f, z);         //Move z units into the screen
    gl.glScalef(0.8f, 0.8f, 0.8f);          //Escalamos para que quepa en la pantalla
    //Rotamos sobre los ejes.
    gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
    gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
    gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z
    //Dibujamos el cuadrado
    square.draw(gl);    
    //Factores de rotación.
    xrot += xspeed;
    yrot += yspeed;
}

正方形の描画:

    public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW);
    //gl.glEnable(GL10.GL_BLEND);
    //Bind our only previously generated texture in this case
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    //Point to our vertex buffer
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    //Enable vertex buffer
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //Draw the vertices as triangle strip
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
    //Disable the client state before leaving
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //gl.glDisable(GL10.GL_BLEND);

}

VERTEXバッファ値:

private FloatBuffer vertexBuffer;

private float vertices[] = 
{ 
    -1.0f, -1.0f, 0.0f,     //Bottom Left
    1.0f, -1.0f, 0.0f,      //Bottom Right
    -1.0f, 1.0f, 0.0f,      //Top Left
    1.0f, 1.0f, 0.0f        //Top Right
};
.
.
.
public Square(int resourceId) {
        ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        vertexBuffer = byteBuf.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
.
.
.
13

最初に知っておくべきことは、OpenGLでは変換行列が右から乗算されるということです。どういう意味ですか?つまり、最後に記述した変換が最初にオブジェクトに適用されます。

コードを見てみましょう:

_gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z
gl.glTranslatef(0.0f, 0.0f, z);

square.draw(gl);    
_

つまり、最初にオブジェクトが_(0.0f, 0.0f, z)_に移動されます。次に、Z、Y、Xの順に回転し、_(0.0f, 0.0f, -z)_で移動して、最後にスケーリングします。

あなたは正しいスケーリングを得ました。最初に配置するため、最後に適用されます。あなたも得た

_gl.glTranslatef(0.0f, 0.0f, -z);
_

最初にオブジェクトを回転させてから移動したいため、適切な場所に配置します。オブジェクトを回転させるときは、常に基本座標、つまり(0、0、0)を中心に回転することに注意してください。独自の軸を中心にオブジェクトを回転させる場合、オブジェクト自体は(0、0、0)になければなりません。

だから、あなたが書く直前

_square.draw(gl);
_

ローテーションが必要です。コードの現在の状態では、オブジェクトを遠くに移動します(

_gl.glTranslatef(0.0f, 0.0f, z);
_

square.draw(gl);)とTHENの前に回転し、混乱を招きます。その行を削除すると、必要なものにはるかに近づきます。したがって、コードは次のようになります。

_gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z

square.draw(gl);    
_

これで正方形が定位置で回転するはずです。

注:これを実行すると、正方形の回転がかなりぎこちなくなることがわかります。たとえば、zを中心に90度回転すると、前回の回転のため、xを中心に回転するとyを中心に回転したように見えます。今のところ、これで問題ないかもしれませんが、見栄えを良くしたい場合は、次のようにしてください。

あなたがオブジェクトを回転させているのではなく、オブジェクトの周りをカメラが回転しているのを想像してみてください。 xrotyrot、およびzrotを変更することにより、オブジェクトの周りの球体上でカメラを移動しています。次に、カメラの位置を見つけたら、計算を実行して正しいパラメーターを取得し、glRotatefおよびglTranslatefを呼び出すか、gluLookAtを使用します。

これには、数学と3Dの想像力をある程度理解する必要があります。ですから、初日に正しく理解できなくても、イライラしないでください。

編集:これは、回転したオブジェクト座標に沿って回転する方法のアイデアです。

まず、zを中心に回転させるとしましょう。したがって、

_gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z
_

ここで、グローバルY単位ベクトルは明らかに(0、1、0)ですが、オブジェクトは回転しているため、itsY単位ベクトルも回転しています。このベクトルは次のように与えられます。

_[cos(zrot) -sin(zrot) 0]   [0]   [-sin(zrot)]
[sin(zrot)  cos(zrot) 0] x [1] = [ cos(zrot)]
[0          0         1]   [0]   [ 0        ]
_

したがって、yを中心とした回転は次のようになります。

_gl.glRotatef(yrot, -sin(zrot), cos(zrot), 0.0f);   //Y-object
_

これまでのところ試してみて(xの周りの回転を無効にして)、それがあなたが望む方法のように見えることを確認できます(私がやったところ、うまくいきました)。

Xの場合、非常に複雑になります。どうして?なぜなら、X単位ベクトルは最初にzベクトルの周りを回転するだけでなく、その後_(-sin(zrot), cos(zrot), 0)_ベクトルの周りを回転するからです。

したがって、オブジェクトの座標のX単位ベクトルは

_                   [cos(zrot) -sin(zrot) 0]   [1]                      [cos(zrot)]
Rot_around_new_y * [sin(zrot)  cos(zrot) 0] x [0] = Rot_around_new_y * [sin(zrot)]
                   [0          0         1]   [0]                      [0        ]
_

このベクトル(u_x、u_y、u_z)を呼びましょう。次に、最終的な回転(Xを中心とした回転)は次のようになります。

_gl.glRotatef(xrot, u_x, u_y, u_z);   //X-object
_

そう!マトリックス_Rot_around_new_y_を見つける方法は?任意の軸を中心とした回転については こちら を参照してください。セクション6.2の最初の行列に移動し、3 * 3サブ行列の回転を取得(つまり、変換に関連する右端の列を無視)し、_(-sin(zrot), cos(zrot), 0)_を_(u, v, w)_軸およびthetayrotとして。

多くの努力を必要とし、結局私はとにかく周りのどこかで間違いをするので、ここでは計算をしません。ただし、非常に注意深く、数回それらを再確認する準備ができている場合は、それを書き留めて行列の乗算を行うことができます。


追記:_Rot_around_new_y_を計算する1つの方法は Quaternions を使用することもできます。四元数は、4Dベクトル_[xs, ys, zs, c]_として定義されます。これは、sinsで、cosが次の角度を持つ_[x, y, z]_を中心とした回転に対応します。 c

この_[x, y, z]_は「新しいY」、つまり[-sin(zrot), cos(zrot), 0]です。角度はyrotです。したがって、Yを中心とした回転の四元数は次のように与えられます。

_q_Y = [-sin(zrot)*sin(yrot), cos(zrot)*sin(yrot), 0, cos(yrot)]
_

最後に、クォータニオン_[a, b, c, d]_がある場合、対応する回転行列 として与えられます

_[1 - 2b^2 - 2c^2        2ab + 2cd           2ac - 2bd   ]
[   2ab - 2cd        1 - 2a^2 - 2c^2        2bc - 2ad   ]
[   2ac - 2bd           2bc + 2ad        1 - 2a^2 - 2b^2]
_
24
Shahbaz

私はopenGLについてはほとんど何も知りませんが、0に変換し、回転してから元に戻すことが機能するはずです...

gl.glTranslatef(-x, -y, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z
gl.glTranslatef(x, y, z);
3
Matt MacLean

やりたいことをやるには四元数が必要だと思います。座標軸を中心とした回転を使用することは時々機能しますが、最終的には「ジンバルロック」の影響を受けます。これは、必要な回転が座標軸の近くを通過し、軸の周りに必要な回転が180度に近づくと、不要な旋回を作成するときに発生します。

クォータニオンは、3Dベクトルとして定義された任意の軸を中心とした回転を表す数学オブジェクトです。 openGLで使用するには、クォータニオンからマトリックスを生成し、モデルビューマトリックスを掛けます。これにより、ワールド座標が変換され、正方形が回転します。

詳細はこちら http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation

Quaternion C++クラスがあり、それが役立つ場合は送信できます。

3
Steve O'Connor

追加してみてください

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

回転されている単一の立方体のレンダリングコードの前

glPopMatrix();

レンダリングが完了した後。これにより、主要なモデルビューマトリックスに影響を与えずに使用できる追加のビューマトリックスが得られます。

基本的に、これは新しいモデルビューカメラを作成し、レンダリングしてから破棄することです。

0
Sean