2

The problem is that when rotating my 3D cube on more than one axes, it distorts weirdly roughly halfway through. I am using the JOML math library for matrices.

// This is the model matrix for the rotation of a textured cube
Matrix4f model = new Matrix4f();
model.identity();
model.rotate((float)(glfwGetTime() * Math.toRadians(50.0f)), new Vector3f(0.5f, 1.0f, 0.0f), model);
// Other matrices for coordinate system
Matrix4f view = new Matrix4f();
view.identity();
view.translate(new Vector3f(0.0f, 0.0f, -3.0f), view);
Matrix4f projection = new Matrix4f();
projection.identity();
projection.perspective((float)Math.toRadians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); // FOV is 45

This is a gif of the distortion:

gif of the distortion

genpfault
  • 51,148
  • 11
  • 85
  • 139

2 Answers2

2

The main problem is that your rotation axis (0.5f, 1.0f, 0.0f) is not unit/normalized, (as is also required by its JavaDoc)

Parameters:

axis - the rotation axis (needs to be normalized)

To solve the problem and still use a matrix, you simply need to normalize the rotation axis.

Also (but this is irrelevant to the error):

  • JOML matrices are identity by default after instantiation - you do not need to call identity() on them)
  • you can omit supplying model as the argument to the dest parameter of rotate()

So:

// This is the model matrix for the rotation of a textured cube
Matrix4f model = new Matrix4f();
model.rotate((float)(glfwGetTime() * Math.toRadians(50.0f)),
             new Vector3f(0.5f, 1.0f, 0.0f).normalize());
httpdigest
  • 5,411
  • 2
  • 14
  • 28
0

There are 2 mistakes in your code:

First: you try to update 2 axis at once. Doing this will cause the model to scale as it rotates.

Second: you don't use 1.0f when defining what axis you want to rotate. This aswell causes the model to scale.

The way Matrix4f.rotate(float angleInRadiants, Vector3f(x, y, z)) works is it will rotate the axis specified in the the vector by the specified angleInRadians.

This is the correct way to rotate both axis:

model
.rotate((float)(glfwGetTime() * Math.toRadians(50.0f)), new Vector3f(0.0f, 1.0f, 0.0f), model)
.rotate(((float)(glfwGetTime() * Math.toRadians(50.0f)) / 2), new Vector3f(0.1f, 0.0f, 0.0f), model);

A better way to do rotation is quaternions.

You can create a new Quaternionf object, set it's angles and rotate the model matrix using it.

float someAngleInRads = (float) Math.toRadians(20f * glfwGetTime());
            
Quaternionf quaternionf = new Quaternionf()
        .rotateAxis(someAngleInRads, new Vector3f(0, 1, 0))
        .rotateAxis(someAngleInRads / 2, new Vector3f(1, 0, 0));
model.rotate(quaternionf);

You could also set the angles for the quaternion this way:

Quaternionf quaternionf = new Quaternionf().rotateXYZ(Math.toRadians(someAngleInRads / 2), Math.toRadians(someAngleInRads), Math.toRadians(0f));

LINCKODE
  • 1
  • 2
  • 1
    *"You try to update 2 axis at once. Doing this will cause the model to scale as it rotates."* - No, that is not true. A rotation matrix does not scale. – Rabbid76 Nov 27 '21 at 21:06
  • As you can see from here https://streamable.com/znpsh3 it does scale if you rotate 2 axis at once and/or you don't normalize. Also using quaternions prevents gimbal lock; – LINCKODE Nov 27 '21 at 21:42
  • I think JOML is doing something weird, i just check learnopengl.com and GLM allows you to rotate 2 axis at once. I remember I had weird distortion issues when i started out too and rotating the axis individually fixed it. – LINCKODE Nov 27 '21 at 21:50
  • Thanks to everyone, I used the quaternion and it worked! I don't know why that rotation matrix was scaling – BerserkGoat8 Nov 27 '21 at 21:57
  • 1
    @BerserkGoat8 You have to normalize the rotation axis with [`normalize`](https://joml-ci.github.io/JOML/apidocs/org/joml/Vector3f.html#normalize()). That's all. – Rabbid76 Nov 27 '21 at 21:58
  • Aaaah thanks - that worked as well! Not sure why java/JOML needs that and GLM doesn't but oh well. – BerserkGoat8 Nov 27 '21 at 22:03