'3D Rotation Matrix deforms over time in Processing/Java

Im working on a project where i want to generate a 3D mesh to represent a certain amount of data.

To create this mesh i want to use transformation Matrixes, so i created a class based upon the mathematical algorithms found on a couple of websites.

Everything seems to work, scale/translation but as soon as im rotating a mesh on its x-axis its starts to deform after 2 to 3 complete rotations. It feels like my scale values are increasing which transforms my mesh. I'm struggling with this problem for a couple of days but i can't figure out what's going wrong.

To make things more clear you can download my complete setup here.

I defined the coordinates of a box and put them through the transformation matrix before writing them to the screen

This is the formula for rotating my object

    void appendRotation(float inXAngle, float inYAngle, float inZAngle, PVector inPivot ) {

    boolean setPivot = false;

    if (inPivot.x != 0 || inPivot.y != 0 || inPivot.z != 0) {
        setPivot = true;
    }

    // If a setPivot = true, translate the position
    if (setPivot) {

        // Translations for the different axises need to be set different
        if (inPivot.x != 0) { this.appendTranslation(inPivot.x,0,0); }
        if (inPivot.y != 0) { this.appendTranslation(0,inPivot.y,0); }
        if (inPivot.z != 0) { this.appendTranslation(0,0,inPivot.z); }

    }

    // Create a rotationmatrix
    Matrix3D rotationMatrix = new Matrix3D();

    // xsin en xcos
    float xSinCal = sin(radians(inXAngle));
    float xCosCal = cos(radians(inXAngle));      
    // ysin en ycos
    float ySinCal = sin(radians(inYAngle));
    float yCosCal = cos(radians(inYAngle));     
    // zsin en zcos
    float zSinCal = sin(radians(inZAngle));
    float zCosCal = cos(radians(inZAngle));           

    // Rotate around x
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[1][1] = xCosCal;
    rotationMatrix.matrix[1][2] = xSinCal;
    rotationMatrix.matrix[2][1] = -xSinCal;
    rotationMatrix.matrix[2][2] = xCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Rotate around y
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[0][0] = yCosCal;
    rotationMatrix.matrix[0][2] = -ySinCal;
    rotationMatrix.matrix[2][0] = ySinCal;
    rotationMatrix.matrix[2][2] = yCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Rotate around z
    rotationMatrix.setIdentity();
    // --
    rotationMatrix.matrix[0][0] = zCosCal;
    rotationMatrix.matrix[0][1] = zSinCal;
    rotationMatrix.matrix[1][0] = -zSinCal;
    rotationMatrix.matrix[1][1] = zCosCal;
    // Add rotation to the basis matrix
    this.multiplyWith(rotationMatrix);

    // Untranslate the position
    if (setPivot) {

        // Translations for the different axises need to be set different
        if (inPivot.x != 0) { this.appendTranslation(-inPivot.x,0,0); }
        if (inPivot.y != 0) { this.appendTranslation(0,-inPivot.y,0); }
        if (inPivot.z != 0) { this.appendTranslation(0,0,-inPivot.z); }

    }

}

Does anyone have a clue?



Solution 1:[1]

You never want to cumulatively transform matrices. This will introduce error into your matrices and cause problems such as scaling or skewing the orthographic components.

The correct method would be to keep track of the cumulative pitch, yaw, roll angles. Then reconstruct the transformation matrix from those angles every update.

Solution 2:[2]

If there is any chance: avoid multiplying rotation matrices. Keep track of the cumulative rotation and compute a new rotation matrix at each step.

If it is impossible to avoid multiplying the rotation matrices then renormalize them (starts on page 16). It works for me just fine for more than 10 thousand multiplications.

However, I suspect that it will not help you, numerical errors usually requires more than 2 steps to manifest themselves. It seems to me the reason for your problem is somewhere else.

Yaw, pitch and roll are not good for arbitrary rotations. Euler angles suffer from singularities and instability. Look at 38:25 (presentation of David Sachs)

http://www.youtube.com/watch?v=C7JQ7Rpwn2k

Good luck!

Solution 3:[3]

As @don mentions, try to avoid cumulative transformations, as you can run into all sort of problems. Rotating by one axis at a time might lead you to Gimbal Lock issues. Try to do all rotations in one go.

Also, bare in mind that Processing comes with it's own Matrix3D class called PMatrix3D which has a rotate() method which takes an angle(in radians) and x,y,z values for the rotation axis.

Here is an example function that would rotate a bunch of PVectors:

PVector[] rotateVerts(PVector[] verts,float angle,PVector axis){
  int vl = verts.length;
  PVector[] clone = new PVector[vl];
  for(int i = 0; i<vl;i++) clone[i] = verts[i].get();
  //rotate using a matrix
  PMatrix3D rMat = new PMatrix3D();
  rMat.rotate(angle,axis.x,axis.y,axis.z);
  PVector[] dst = new PVector[vl];
  for(int i = 0; i<vl;i++) {
    dst[i] = new PVector();
    rMat.mult(clone[i],dst[i]);
  }
  return dst;
}

and here is an example using it.

rotation matrix

HTH

Solution 4:[4]

A shot in the dark: I don't know the rules or the name of the programming language you are using, but this procedure looks suspicious:

void setIdentity() {  
    this.matrix = identityMatrix;
}

Are you sure your are taking a copy of identityMatrix? If it is just a reference you are copying, then identityMatrix will be modified by later operations, and soon nothing makes sense.

Solution 5:[5]

Though the matrix renormalization suggested probably works fine in practice, it is a bit ad-hoc from a mathematical point of view. A better way of doing it is to represent the cumulative rotations using quaternions, which are only converted to a rotation matrix upon application. The quaternions will also drift slowly from orthogonality (though slower), but the important thing is that they have a well-defined renormalization.

Good starting information for implementing this can be:

A useful academic reference can be:

  • K. Shoemake, “Animating rotation with quaternion curves,” ACM SIGGRAPH Comput. Graph., vol. 19, no. 3, pp. 245–254, 1985. DOI:10.1145/325165.325242

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 don
Solution 2
Solution 3 George Profenza
Solution 4 antonakos
Solution 5 Ludvig