How to rotate a graphic over global axes, and not

2019-08-07 16:57发布

问题:

I think my problem is very simple, but I´m new in Opengl and I don't know what to do.

I'm trying to rotate a pyramid (the figure doesn't matter), I want to rotate with respect to X and then rotate with respect to Y axe.

But when I do a rotation on X, the Y axe rotates too. And then when I want to rotate whit respect to Y, Y axis is no longer the original.

The next image shows how mi pyramid stars in the origin:

Then rotate winth respect to X:

The axes Y and Z rotate too, and then if I want to rotate with respect to Y, the pyramid rotates with respect to the Y axe rotated, and not to the original Y axe(strong green).

I use PushMatrix(); and PopMatrix(); :

gl.PushMatrix();             
    gl.Rotate(bx, 1.0f, 0.0f, 0.0f);           

        gl.PushMatrix();
        gl.Rotate(by, 0.0f, 1.0f, 0.0f); 

            //Pyramid                      
            gl.Begin(OpenGL.GL_TRIANGLES);
            gl.Color(1.0f, 0.0f, 0.0f);
            gl.Vertex(0.0f, 1.0f, 0.0f);
            gl.Vertex(-1.0f, -1.0f, 1.0f);
            gl.Vertex(1.0f, -1.0f, 1.0f);

             gl.Color(0.0f, 1.0f, 0.0f);
             gl.Vertex(0.0f, 1.0f, 0.0f);
             gl.Vertex(1.0f, -1.0f, 1.0f);
             gl.Vertex(1.0f, -1.0f, -1.0f);

             gl.Color(0.0f, 0.0f, 1.0f);
             gl.Vertex(0.0f, 1.0f, 0.0f);
             gl.Vertex(1.0f, -1.0f, -1.0f);
             gl.Vertex(-1.0f, -1.0f, -1.0f);

             gl.Color(1.0f, 1.0f, 1.0f);
             gl.Vertex(0.0f, 1.0f, 0.0f);
             gl.Vertex(-1.0f, -1.0f, -1.0f);
             gl.Vertex(-1.0f, -1.0f, 1.0f);
             gl.End();

         gl.PopMatrix();
   gl.PopMatrix();

bx and by just increment or decrement the angle when you click the button (Z doesn't matter now)

Well I want to rotate the pyramid, with respect to X and Y, but the original axes, (universal axes) independently of local axes.

Note: I use SharpGL (OpenGL for c#), but it's the same.

Update

I change my code:

gl.PushMatrix();             
    gl.Rotate(bx, 1.0f, 0.0f, 0.0f);           

      gl.PushMatrix();
       //gl.Rotate(by, 0.0f, 1.0f, 0.0f); This changes
        gl.Rotate(by, 0.0f, Math.Cos(bx*Math.PI / 180), -Math.Sin(bx*Math.PI / 180));           
         //Pyramid                      

      gl.PopMatrix();
 gl.PopMatrix();

I multiply the matrix Rx inverse [1 0 0; 0 Cos Sin; 0 -Sin Cos] by the vector Y [0 1 0], and the resulting vector [0 Cos -Sin], I put in the rotation by Y.

This works as I want, I do a rotation by X, after a rotation by Y, but when I do another rotation by X, now the rotation is by the X rotated and not with respect to the original!!!, but the rotation by Y keeps as I want.

Any another idea?? Thank you so much!!!

回答1:

Order of operations matter and by the semantics used in OpenGL the actual transformations are applied in the reverse order as they happen in the code. So if you want to first rotate by X, then by Y, the Y rotation must come first in your code.

The deeper reason for this is that OpenGL uses (for various, sensible reasons) column major, right associative matrix ordering. A transformation chain like

MV = V · M · R_x · R_y

Will multiply in a column (position) vector from the right toward left, i.e.

MV · ↑v

decomposes into

V · ( M · ( R_x · ( R_y · ↑v ) ) )

And since matrices are not abelian there is are uncountably many matrices A, B for which

A · B =/= B · A


回答2:

I have run into this recently and ended up with this solution:

  1. Rotate about local x-axis with glRotate(bx,1f,0f,0f);.
  2. Calculate global y-axis components in the local coordinate system ux=0, uy=cos(bx), uz=sin(bx).
  3. Rotate about the local axis with glRotate(by,ux,uy,uz);.

Note that the functions needed are

Func<float,float> cos = (x)=>(float)(Math.Cos(x*Math.PI/180));
Func<float,float> sin = (x)=>(float)(Math.Sin(x*Math.PI/180));

to convert the angles to radians, take the sin and cos and return a float.