3
\$\begingroup\$

Is there a general method to convert a matrix transformation from one coordinate system to another, so that the resulting transformation looks the same on screen?

For example: There are some transformations in a coordinate system with X right, Y up, and Z toward the viewer. And they need to be converted to a coordinate system with X right, Y away from the viewer, and Z up.

enter image description here

What would be the operation that needs to be performed for each matrix so that the transformations look the same in the other coordinate system? And is there a general way to construct this operation given the source and destination basis vectors?

\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

This is as easy as writing your old coordinates in terms of the new ones.

  • We want +x to map to +x (1, 0, 0)

  • We want +y to map to +z (0, 0, 1)

  • We want +z to map to -y (0, -1, 0)

  • We want the fourth, homogenous coordinate to survive unchanged (0, 0, 0, 1)

So you make those vectors the columns of a coordinate conversion matrix:

$$\Bbb C = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

If you left-multiply this matrix by any homogeneous vector in your old coordinate system, it converts it to the corresponding vector in the new coordinate system:

$$\vec v_{\text{new}} = \Bbb C \times \vec v_{\text{old}}$$

And the same goes for any vector transformed by a transformation matrix \$\Bbb M\$ expressed in your old space:

$$ \vec v_{\text{new}} = \Bbb C \times \vec v_{\text{transformed}} = \Bbb C \times (\Bbb M \times \vec v_{\text{untransformed}})\\ \vec v_{\text{new}} = (\Bbb C \Bbb M) \times \vec v_{\text{untransformed}}$$

So you can multiply any transformation matrix by this coordinate transformation matrix to get a single matrix that does both the original transformation and the coordinate conversion.

If you use the opposite multiplication convention - vector on the left, matrix on the right - then take the transpose of \$\Bbb C\$ (so your destination vectors are the rows instead of the columns) and multiply it on the right instead of the left.


You can use this same logic to work with untransformed vectors already in the new coordinate system: just convert them back to the old coordinate system (using the inverse of matrix \$\Bbb C\$ above, \$\Bbb C^{-1}\$), use the transformation matrix from the old system (\$\Bbb M\$), and then convert back:

$$\begin{align} \vec v_\text{transformed-new} &= \Bbb C \times \vec v_\text{transformed-old} \\ &= \Bbb C \times (\Bbb M \times \vec v_\text{untransformed-old}) \\ &= (\Bbb C \Bbb M) \times (\Bbb C^{-1} \times \vec v_\text{untransformed-new}) \\ &= (\Bbb C \Bbb M \Bbb C^{-1}) \times \vec v_\text{untransformed-new} \\ \end{align}$$

So, your matrix that does the same job as \$\Bbb M\$ but in the new coordinate system is just \$\Bbb M_\text{new} = \Bbb C \Bbb M \Bbb C^{-1}\$.

Since your coordinate transformation is pure rotation/reflection - no scaling/shearing - the inverse of \$\Bbb C\$ is just its transpose (making its rows into columns and vice versa):

$$\Bbb C^{-1} = \Bbb C^T = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & -1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

\$\endgroup\$
3
  • \$\begingroup\$ Thanks, after implementing and optimizing it, I've added the complete solution including the C# code as another answer. It does everything very efficiently with only 6 multiplications and one division. \$\endgroup\$
    – thalm
    Commented Oct 27, 2022 at 10:53
  • \$\begingroup\$ Thanks for your answer. Any pointers on how translations would be handled - i.e. where the "origins" of the two coordinate systems are different? \$\endgroup\$
    – BenKoshy
    Commented Jul 31 at 23:20
  • 1
    \$\begingroup\$ Easy: express the new coordinate system's origin in terms of the old, then run that point through the matrix above. Negate the result and use that as your fourth column (keeping a 1 in the bottom-right entry). Now if you pass that new-origin-in-old-system point through the updated matrix, the result gets subtracted from itself, yielding the zero vector. \$\endgroup\$
    – DMGregory
    Commented Aug 1 at 7:53
1
\$\begingroup\$

Final solution with code and optimization:

Here is the code that is doing the conversion in one go. It includes:

  • Conversion to destination system as described in the answer by DMGregory
  • Uniform scaling from meters to millimeters
  • Transpose, because the matrix layout of the destination software is different

All of these steps combined in one efficient method look like this:

public static Transform MatrixToXForm(ref Matrix m, float s = 1000)
{
    var rs = 1.0f / s;    
    var t = new Transform();

    t.M00 =  m.M11;      t.M01 = -m.M31;      t.M02 =  m.M21;      t.M03 =  m.M41 * s;
    t.M10 = -m.M13;      t.M11 =  m.M33;      t.M12 = -m.M23;      t.M13 = -m.M43 * s;
    t.M20 =  m.M12;      t.M21 = -m.M32;      t.M22 =  m.M22;      t.M23 =  m.M42 * s;
    t.M30 =  m.M14 * rs; t.M31 = -m.M34 * rs; t.M32 =  m.M24 * rs; t.M33 =  m.M44;

    return t;
}

The interesting point here is that the "matrix sandwich" \$\Bbb M_\text{new} = \Bbb C \Bbb M \Bbb C^{-1}\$ cannot be expressed as one matrix multiplication, but it can be written as code that shuffles the elements and signs.

If the two coordinate systems only differ in swapped axes and uniform scaling (here from the software vvvv to Rhino3d), you can derive the code for an efficient direct method like this:

  • Construct the conversion matrix \$\Bbb C \$ using the basis vectors. In this case:

$$\Bbb C = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & -1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

  • Use an example transformation for \$\Bbb M \$. I've just used the row/col index of the elements:

$$\Bbb M = \begin{bmatrix} 11 & 12 & 13 & 14 \\ 21 & 22 & 23 & 24 \\ 31 & 32 & 33 & 34 \\ 41 & 42 & 43 & 44 \end{bmatrix}$$

Then do the \$\Bbb M_\text{new} = \Bbb C \Bbb M \Bbb C^{-1}\$ operation and see where the elements land and what sign they have:

vvvv patch showing step 2

By reading the index numbers, this can be directly converted into code. The full chain of operations including uniform scaling and transpose looks like this and results in the code above:

vvvv patch showing step 3

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .