简体   繁体   中英

Rotation of object around World Axis OpenGL

I'm trying to make a controllable ball in OpenGL. I'm using my own matrix class to transform the object matrix, but I can't seem to get the Rotation right. I always end up with the ball rotating around the local axis. This is how it looks right now https://gfycat.com/LongKindBassethound . The long line are the local axis.

So when the ball moves forward the next side movement will be wrong. Theres a function in the matrix class that allows rotation around any axis:

Matrix& Matrix::rotationAxis(const Vector& Axis, float Angle) {
const float Si = sin(Angle);
const float Co = cos(Angle);
const float OMCo = 1 - Co;
Vector Ax = Axis;
Ax.normalize();

m00= (Ax.X * Ax.X) * OMCo + Co;
m01= (Ax.X * Ax.Y) * OMCo - (Ax.Z * Si);
m02= (Ax.X * Ax.Z) * OMCo + (Ax.Y * Si);
m03= 0;

m10= (Ax.Y * Ax.X) * OMCo + (Ax.Z * Si);
m11= (Ax.Y * Ax.Y) * OMCo + Co;
m12= (Ax.Y * Ax.Z) * OMCo - (Ax.X * Si);
m13= 0;

m20= (Ax.Z * Ax.X) * OMCo - (Ax.Y * Si);
m21= (Ax.Z * Ax.Y) * OMCo + (Ax.X * Si);
m22= (Ax.Z * Ax.Z) * OMCo + Co;
m23= 0;

m30= 0;
m31= 0;
m32= 0;
m33= 1;

return *this;
}

I think with this I can take the world direction vectors and transform them to the local space of the object and then rotate around the result. I don't really know how to do that though (matrix of the ball * world vector? That doesn't work). I would really like to avoid quaternions, but if I can't do that I would appreciate suggestions in that direction too.

EDIT: More Info

The transforamtion Code. As you can see I tried different methods that all do the same... So no surprise there that it doesnt work.

Matrix transM, rotX, rotZ;
rotationX = straight;
rotationZ = side;
if (velocity != Vector(0, 0, 0)) {
    velocity.X = -0.0005 * DeltaTime;
    velocity.X = clamp(velocity.X, 0, FLT_MAX);

    velocity.Z = -0.0005 * DeltaTime;
    velocity.Z = clamp(velocity.Z, 0, FLT_MAX);
}

velocity.X += speed * -side * DeltaTime;
velocity.Z += speed * straight * DeltaTime;
transM.translation(velocity.X, 0, velocity.Z);

if (velocity.Z != 0 || velocity.X != 0) {
    //http://mathworld.wolfram.com/EulerAngles.html
    //http://gamedev.stackexchange.com/questions/67199/how-to-rotate-an-object-around-world-aligned-axes

    Vector localAxisX =  m_Ball * Vector(1, 0, 0);
    Vector localAxisZ = m_Ball * Vector(0, 0, 1);
    rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime);
    rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime);
    //rotX.rotationX(0.5* M_PI * straight * DeltaTime * 3);
    //rotZ.rotationZ(0.5* M_PI * side * DeltaTime * 3);
    //Matrix fullRotation.rotationYawPitchRoll(Vector(0,  0.5* M_PI * straight * DeltaTime, 0.5* M_PI * side * DeltaTime));
    m_Ball = transM * m_Ball * (rotX*rotZ);
}
else {
    m_Ball = transM * m_Ball;
}

Draw code with my previous attempt trying to use glRotatef (obviously commented out right now)

void Ball::draw(float DeltaTime) {
  glPushMatrix();
  glMultMatrixf(m_Ball);
  if(rotationX)
    glRotatef(0.5* M_PI * rotationX * DeltaTime, 1.0, 0.0, 0.0);
  if(rotationZ)
    glRotatef(0.5* M_PI * rotationZ * DeltaTime, 0.0, 0.0, 1.0);
  g_Model_ball.drawTriangles();
  glPopMatrix();
  drawAxis();
}

I highly suggest using quaternions to easily handle compound rotations and avoid gimbal lock.

https://en.wikipedia.org/wiki/Gimbal_lock

Ok with regards to your comments and video, You want to rotate around the ball's center. It seems you accumulate your rotations in m_Ball but do a weird transM multiplication. Also you are probably accumulating translations in transM .

Try not to mix your translations and rotations and avoid accumulating them in your m_Ball . You can do something like this.

//transformation matrix
transM.translation(velocity.X, 0, velocity.Z);

//accumulate translations in m_BallT
m_BallT = transM * m_BallT;

//final Rotation
Matrix rotation = rotX * rotZ;

//accumulate rotations in m_BallR
m_BallR = rotation * m_BallR;

//now compute your final transformation matrix from accumulated rotations and translation
m_Ball = m_BallT * m_BallR;

note how m_BallR is just rotations accumulated. Post multiplication will ensure new rotation is applied after accumulated rotations in m_BallR . Finally translate to the final position accumulated in m_BallT . Your ball will rotate about its center and move according to m_BallT .

You could also simply replace the transformation component on your m_BallR to avoid extra matrix multiplications.

Vector newPos(m_Ball.translation().X + velocity.X, terrainNoise.GetHeight(m_Ball.translation().X, m_Ball.translation().Z) + 0.5, m_Ball.translation().Z + velocity.Z);

rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime * abs(velocity.Z) * 100);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime * abs(velocity.X) * 100);

m_Rotation = (rotX*rotZ);

m_Ball = (m_Ball.invert() * m_Rotation).invert();


m_Ball.m03 = newPos.X;
m_Ball.m13 = newPos.Y;
m_Ball.m23 = newPos.Z;

This is the solution I came up with after reading this link provided by @Spektre. Basically you just invert the ModelMatrix of the ball to get it into world position, do your rotation and then transform it back into local space.

You have to set the newPos Vector before you rotate, otherwise it would affect future transformations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM