Amit Assaraf
Amit Assaraf

Reputation: 504

OpenGL Shader - Rotating a model around its origin (2D World)

So I created a vertex shader that takes in an angle and calculates the rotation. There is a problem though that the model rotates around the world center and not its own axis/origin.

Side note: This is 2D rotation.

How do I make the model rotate through its own axis?

Here is my current vertex shader:

#version 150 core

in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;

out vec4 pass_Color;
out vec2 pass_TextureCoord;

void main(void) {


    gl_Position = in_Position;

    pass_Color = in_Color;
    pass_TextureCoord = in_TextureCoord;
}

Rotating CPU side:

    Vector3f center = new Vector3f(phyxBody.getPosition().x,phyxBody.getPosition().y,0);
    Matrix4f pos = new Matrix4f();
    pos.m00 = (phyxBody.getPosition().x)-(getWidth()/30f/2f);
    pos.m01 = (phyxBody.getPosition().y)+(getHeight()/30f/2f);
    pos.m10 = (phyxBody.getPosition().x)-(getWidth()/30f/2f);
    pos.m11 = (phyxBody.getPosition().y)-(getHeight()/30f/2f);
    pos.m20 = (phyxBody.getPosition().x)+(getWidth()/30f/2f);
    pos.m21 = (phyxBody.getPosition().y)-(getHeight()/30f/2f);
    pos.m30 = (phyxBody.getPosition().x)+(getWidth()/30f/2f);
    pos.m31 = (phyxBody.getPosition().y)+(getHeight()/30f/2f);

    pos.rotate(phyxBody.getAngle(),center);

Result is a weird rotated stretch of the object.. Do you know why? Don't worry about the /30f part.

EDIT:

    Vector3f center = new Vector3f(0,0,0);
    Matrix4f pos = new Matrix4f();
    pos.m00 = -(getWidth()/30f/2f);
    pos.m01 = +(getHeight()/30f/2f);
    pos.m10 = -(getWidth()/30f/2f);
    pos.m11 = -(getHeight()/30f/2f);
    pos.m20 = +(getWidth()/30f/2f);
    pos.m21 = -(getHeight()/30f/2f);
    pos.m30 = +(getWidth()/30f/2f);
    pos.m31 = +(getHeight()/30f/2f);

    pos.rotate(phyxBody.getAngle(),center);

    pos.m00 += phyxBody.getPosition().x;
    pos.m01 += phyxBody.getPosition().y;
    pos.m10 += phyxBody.getPosition().x;
    pos.m11 += phyxBody.getPosition().y;
    pos.m20 += phyxBody.getPosition().x;
    pos.m21 += phyxBody.getPosition().y;
    pos.m30 += phyxBody.getPosition().x;
    pos.m31 += phyxBody.getPosition().y;

This is currently the transformation code, yet the rotation still doesn't work correctly.

My try at the rotate method: (What am I doing wrong?)

    if (phyxBody.getAngle() != 0.0) {
        pos.m00 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
        pos.m01 *= Math.sin(Math.toDegrees(phyxBody.getAngle()));
        pos.m10 *= -Math.sin(Math.toDegrees(phyxBody.getAngle()));
        pos.m11 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
        pos.m20 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
        pos.m21 *= Math.sin(Math.toDegrees(phyxBody.getAngle()));
        pos.m30 *= -Math.sin(Math.toDegrees(phyxBody.getAngle()));
        pos.m31 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
    }

Upvotes: 2

Views: 4203

Answers (2)

GraphicsMuncher
GraphicsMuncher

Reputation: 4641

The order is scaling * rotation * translation - see this question. I'm guessing you've already translated your coordinates outside of your shader. You'll have to rotate first, then translate. It's good to know the linear algebra behind what you're doing so you know why things work or don't work.

The typical way to do this is to pass a pre-computed ModelView matrix that has already taken care of scaling/rotation/translation. If you've already translated your vertices, you can't fix the problem in your shader without needlessly undoing it and then redoing it after. Send in your vertices untranslated and accompany them with data, like your angle, to translate them. Or you can translate and rotate both beforehand. It depends on what you want to do.

Bottom line: You must rotate before you translate.

Here is the typical way you do vertex transformations:

OpenGL side:

  1. Calculate ModelView matrix: Scale * Rotation * Translation

  2. Pass to shader as a uniform matrix

GLSL side:

  1. Multiply vertices by ModelView matrix in vertex shader

  2. Send to gl_Position

Response to Edit:

I'm inclined to think your implementation needs to be completely redone. You have points that belong to a model. These points are all oriented around the origin. For example, if you had a car, the points would form a mesh of triangles.

  • If you simply do not translate these points and then rotate them, the car will rotate around its center. If you translate afterwards, the car will translate in its rotated fashion to the place you've specified. The key here is that the origin of your model lines up with the origin of rotation so you end up rotating the model "around itself."

  • If you instead translate to the new position and then rotate, your model will rotate as if it were orbiting the origin. This is probably not what you want.

  • If you're modifying the actual vertex positions directly instead of using transformation matrices, you're doing it wrong. Even if you just have a square, leave the coordinates at (-1,-1) (-1,1) (1,1) (1,-1) (notice how the center is at (0,0)) and translate them to where you want to be.

  • You don't have to re-implement math functionality and probably shouldn't (unless your goal is explicitly to do so). GLM is a popular math library that does everything you want and it's tailored specifically for OpenGL.

Final Edit

Here is a beautiful work of art I drew for you demonstrating what you need to do.enter image description here

  • Notice how in the bottom right the model has been swept out around the world origin about 45 degrees. If we went another 45, it would have its bottom edge parallel to the X-axis and intersecting the positive Y-axis with the blue vertex in the bottom left and purple vertex in the bottom right.

  • You should probably review how to work with vertices, matrices, and shaders. Vertices should be specified once, matrices should be updated every time you chance the scale, rotation, or position of the object, and shaders should multiply the each vertex in the model by a uniform (constant).

Upvotes: 5

Nicol Bolas
Nicol Bolas

Reputation: 473946

Your sprite lacks sufficient information to be able to do what you're trying to do. In order to compute a rotation about a point, you need to know what that point is. And you don't.

So if you want to rotate about an arbitrary location, you will need to pass that location to your shader. Once there, you subtract it from your positions, rotate the position, and add it back in. However, that would require a lot of work, which is why you should just compute a matrix on the CPU to do all of that. Your shader would be given this matrix and perform the transform itself.

Of course, that itself requires something else, because you keep updating the position of these objects by offsetting the vertices on the CPU. This is not good; you should be keeping these objects relative to their origin in the buffer. You should then transform them to their world-position as part of their matrix.

So your shader should be taking object-relative coordinates, and it should be passed a matrix that does a rotation followed by a translation to their world-space position. Actually, scratch that; the matrix should transform to their final camera-space position (world-space is always a bad idea).

Upvotes: 2

Related Questions