Rotate Object around origin as it faces origin in

2019-08-08 02:27发布

I'm trying to make a simple animation where an object rotates around the world origin in OpenGL using glm lib. My ideia is:

  • Send object to origin

  • Rotate it

  • Send back to original position

  • Make it look at what I want

Here's my implementation:

// Rotates object around point p
void rotate_about(float deltaTime, glm::vec3 p, bool ended) {

    glm::vec3 axis = glm::vec3(0,1,0); //rotation axis
    glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale, scale, scale)); //scale matrix
    glm::mat4 rotation = getMatrix(Right, Up, Front, Position); //builds rotation matrix

    rotation = glm::translate(rotation, p - Position );
    rotation = glm::rotate(rotation, ROTATION_SPEED * deltaTime, axis);
    rotation = glm::translate(rotation, Position - p );

    Matrix = rotation * scale_m;

    //look at point P
    Front = glm::normalize(p - start_Position);
    Right = glm::normalize(glm::cross(WorldUp, Front));
    Up = glm::normalize(glm::cross(Right, Front));

    if (ended == true) { //if last iteration of my animation: saves position
        Position.x = Matrix[3][0];
        Position.y = Matrix[3][1];
        Position.z = Matrix[3][2];  
    }
}

getMatrix() simply returns a 4x4 matrix as:

| Right.x Right.y Right.z |
| Up.x    Up.y    Up.z    |
| Front.x Front.y Front.z |
| Pos.x   Pos.y   Pos.z   |

I'm using this image as reference:

enter image description here

As it is my model simply disappears when I start the animation. If I remove lines bellow "//look at point P" it rotates around the origin, but twitches every time my animation restarts. I'm guessing I'm losing or mixing informations I shouldn't somewhere. How can I store my models Front/Right/Up information so I can rebuild its matrix from scratch?

First edit, this is the effect I'm having when I don't try to make my model look at the point P, in this case the origin. When I do try my model disappears. How can I make it look at where I want, and how can I get my models new Front/Right/Up vectors after I finish rotating it?

Run example

This is the code I ran in the gif above

2条回答
▲ chillily
2楼-- · 2019-08-08 02:37

SOLUTION:

The problem was in this part:

rotation = glm::translate(rotation, p - Position ); 
rotation = glm::rotate(rotation, ROTATION_SPEED * deltaTime, axis);
rotation = glm::translate(rotation, Position - p );


if (ended == true) { //if last iteration of my animation: saves position
    Position.x = Matrix[3][0];
    Position.y = Matrix[3][1];
    Position.z = Matrix[3][2];  
}

Note that I was using the distance between world origin and model as the radius of the translation. However, after the animation ends I update the models Position, which changes the result of p - Position, i.e, the orbit radius. When this happen the model "twitches", because it lost rotation information.

I solved it by using a different variable for the orbit radius, and applying the translation on the z-axis of the model. When the translation is applied on the x-axis, the model - which faces the camera initially - will end up sideways to the origin. However, applying the translation on the z-axis will end up with the model either facing or backwards to the origin, depending on the signal.

查看更多
疯言疯语
3楼-- · 2019-08-08 02:50

Operations like glm::translate() or glm::roate() build a matrix by its parameters and multiply the input matrix by the new matrix

This means that

rotation = glm::translate(rotation, Position - p );

can be expressed as (pseudo code):

rotation = rotation * translation(Position - p);

Note, that the matrix multiplication has to be "read" from the left to the right. (See GLSL Programming/Vector and Matrix Operations)

The operation translate * rotate causes a rotation around the origin of the object:

The operation rotate * translate causes a rotation around the origin of the world:

The matrix glm::mat4 rotation (in the code of your question) is the current model matrix of your object.
It contains the position (translation) and the orientation of the object.
You want to rotate the object around the origin of the world.

To do so you have to create a matrix which contains the new rotation

glm::mat4 new_rot = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, axis);

Then you can calculate the final matrix as follows:

Matrix = new_rot * rotation * scale_m;

If you want to rotate an object around the a point p and the object should always face a point p, then all you need is the position of the object (start_position) and the rotation axis. In your case the rotation axis is the up vector of the world.

glm::vec3 WorldUp( 0.0f, 1.0f, 0.0f );
glm::vec3 start_position = ...;
float scale = ...;
glm::vec3 p = ...;

Calculate the rotation matrix and the new (rotated) position

glm::mat4 rotate    = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, WorldUp);
glm::vec4 pos_rot_h = rotate * glm::vec4( start_position - p, 1.0f );
glm::vec3 pos_rot   = glm::vec3( pos_rot_h ) + p;

Calculate the direction in which the object should "look"

glm::vec3 Front    = glm::normalize(p - pos_rot);

You can use your function getMatrix to setup the current orientation matrix of the object:

glm::vec3 Right    = glm::normalize(glm::cross(WorldUp, Front));
glm::mat4 pos_look = getMatrix(Right, WorldUp, Front, pos_rot);

Calculate the model matrix:

glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale));
Matrix = pos_look * scale_m;

The final code may look like this:

glm::mat4 getMatrix(const glm::vec3 &X, const glm::vec3 &Y, const glm::vec3 &Z, const glm::vec3 &T)
{
    return glm::mat4(
        glm::vec4( X, 0.0f ),
        glm::vec4( Y, 0.0f ),
        glm::vec4( Z, 0.0f ),
        glm::vec4( T, 1.0f ) );
}

void rotate_about(float deltaTime, glm::vec3 p, bool ended) {

    glm::mat4 rotate    = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, WorldUp);
    glm::vec4 pos_rot_h = rotate * glm::vec4( start_position - p, 1.0f );
    glm::vec3 pos_rot   = glm::vec3( pos_rot_h ) + p;

    glm::vec3 Front    = glm::normalize(p - pos_rot);
    glm::vec3 Right    = glm::normalize(glm::cross(WorldUp, Front));
    glm::mat4 pos_look = getMatrix(Right, WorldUp, Front, pos_rot);

    glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale));
    Matrix = pos_look * scale_m;

    if ( ended == true )
        Position = glm::vec3(Matrix[3]);
}
查看更多
登录 后发表回答