11

Transformation matrices in Blender (skip if you want)

Transformation matrices in Blender are by design limited to a certain type of 4x4-matrix that is less general than just any old invertible matrix. For example, shear operations are not possible. Blender's transformation matrices are precisely the matrices that satisfy the following conditions, I think:

  1. The first three columns are mutually perpendicular and end in a 0.
  2. The last column is of the form (a,b,c,1) where (a,b,c) represents a translation.

(I'm using the convention that a transformation matrix M is applied to column vectors v as M*v, as is usual in mathematics.)


My question

Suppose that M is the transformation matrix of a Blender object (and therefore satisfied the conditions above). I now apply a scaling of factor 3 along the global x-axis. How is the resulting transformation matrix calculated?

One would naively expect that the first row of M just gets multiplied by 3. (As opposed to scaling by 3 along the local x-axis, in which case the first column of M gets multiplied by 3.) But this is not what happens, as the resulting matrix would not satisfy the first condition above anymore. Instead, all of the columns of M get rescaled in some way.

How is the transformation matrix of an object changed when I scale along the global x-axis?


An illustration

Start with the default cube. Rotate it by 30 degrees along the y-axis (ry30Enter). Now rescale by 3 in the x-direction (sx3Enter). The object gets rescaled by 2.646 in its local x-direction and by 1.732 in the local z-direction. Where do these numbers come from? (They seem to equal sqrt(7) and sqrt(3), respectively. So their squares sum to 1+3^2, which is probably relevant.)


Observations (added)

If we rescale along a global axis by a factor x, and this results in rescaling along the local axes by factors (a,b,c), then we always have a^2+b^2+c^2 = x^2+1^2+1^2.

Also, rescaling along global x by 3 twice is not the same thing as rescaling along global x by 9.


Note: I don't need an explanation of the mathematics behind transformations, matrices or quaternions. I'm familiar with those.

Daan Michiels
  • 320
  • 1
  • 12
  • I'm personally ignorant to dealing with these transformations at a matrix level, however I did have a thought here. Have you tried applying the scale and rotation 1st, and then looking at the matrix again to see if it was what you would expect? Just curious. – Rick Riggs Apr 29 '16 at 04:50
  • @RickRiggs - Applying the scaling first and then rotating is the same as first rotating and then scaling along the local x-axis (by hitting x twice), as one would expect. But I'm not sure how exactly the numbers 2.646 and 1.732 are calculated. – Daan Michiels Apr 29 '16 at 17:20
  • As another illustration, start from the default cube. Rotate it by 45 degrees about the y-axis. Now scaling along the global x-axis is the same as scaling along the global z-axis (!). – Daan Michiels Apr 29 '16 at 17:21
  • Maybe we are still talking two different things, and I want to make sure we are not. When I say "apply" I mean (with your mesh of focus selected) Ctrl+A >> Rotation & Scale. It sounds like maybe you were thinking "apply" means entering your rotation and scale values to the matrix directly. If this is the case, take my definition of it into account, and check your mesh matrix again to see if you get your expected values. – Rick Riggs Apr 30 '16 at 00:03
  • 1
    @RickRiggs - Oh, sorry, I didn't get what you meant. In any case, applying (ctrl+a) the rotation and scale just applies the object's transformation to the mesh data and resets the object's transformation to the identity. This is what I would expect. But it doesn't answer the question, unfortunately. – Daan Michiels Apr 30 '16 at 00:13
  • Darn, at least we ruled that one out. – Rick Riggs Apr 30 '16 at 04:21
  • I suspected the scaling vector (ie world X axis) is divided into the local X, Y, Z components by projecting onto them and each of the object's scale is multiplied by the respective components but I'm yet to try this theory and make it into an answer post. The algebraic formulas themselves are probably too complex for me, I'm not too good with vector math but at least I would like to demonstrate it graphically. – kheetor Apr 30 '16 at 08:13
  • @kheetor - Yes, it might be something like that. I'll play around with it a little to check whether it's true. – Daan Michiels Apr 30 '16 at 15:06
  • An indication seems to be that 1.732^2 + 2.646^2 = 1 + 3^2. – Daan Michiels Apr 30 '16 at 15:08
  • http://blender.stackexchange.com/questions/28778/hexagon-is-distorting-on-x-axis-when-scaling-on-y-globally/28783#28783 – Chebhou Apr 30 '16 at 18:05
  • @Chebhou - Thanks! That is indeed the same phenomenon. Would you happen to know how it works quantitatively, though? – Daan Michiels May 04 '16 at 02:30

3 Answers3

2

I wondered the same, and when I saw that there was no answer here, I looked it up in the source. Dealing with signs is complicated, but without considering signs, the method is as follows. The sources work with 3x3 matrices (position doesn't influence scale), so I'll do the same here.

Let $S$ be the original scaling matrix, and $R$ be the original rotation matrix. Now, to scale by a new matrix $N$, calculate $A = S\times R\times N$ and obtain the new sizes on each axis by calculating the Euclidean norm of each row of $A$.

In your case:

Suppose that M is the transformation matrix of a Blender object (and therefore satisfied the conditions above). I now apply a scaling of factor 3 along the global x-axis. How is the resulting transformation matrix calculated?

Take the $3\times 3$ matrix $M' = S\times R$ by discarding the last row and column of $M$, then obtain $A = M'\times\left[\begin{matrix}3 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{matrix}\right]$. Now, the new X scale is $\sqrt{a_{11}^2 + a_{12}^2 + a_{13}^2}$, the new Y scale is $\sqrt{a_{21}^2 + a_{22}^2 + a_{23}^2}$, and the new Z scale is $\sqrt{a_{31}^2 + a_{32}^2 + a_{33}^2}$. Use these as the elements of the diagonal of the new scaling matrix, $S'$. The new $3\times3$ matrix is obtained as $S'\times R$ and the $4\times4$ matrix by adding back the last row and column of the original $4\times4$ matrix.

For your example:

Start with the default cube. Rotate it by 30 degrees along the y-axis (ry30Enter). Now rescale by 3 in the x-direction (sx3Enter).

we have $S = I$ (no scaling on the cube to start with), $R = \left[\begin{matrix}\cos 30°&0&\sin 30°\\0&1&0\\-\sin 30°&0&\cos 30°\end{matrix}\right] = \left[\begin{matrix}\sqrt3/2&0&1/2\\0&1&0\\-1/2&0&\sqrt3/2\end{matrix}\right]$ and $N = \left[\begin{matrix}3 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{matrix}\right]$. Now $A = S\times R\times N = \left[\begin{matrix}3\sqrt3/2&0&1/2\\0&1&0\\-3/2&0&\sqrt3/2\end{matrix}\right]$ so the new scale on X is $\sqrt{(3\sqrt3/2)^2+(1/2)^2} = \sqrt7\approx 2.646$, on Y is 1, and on Z is $\sqrt{(-3/2)^2+(\sqrt3/2)^2} = \sqrt3\approx 1.732$.

The code can be found in source/blender/editors/transform/transform.c in function ElementResize(). Function TransMat3ToSize() extracts the norm of each row and deals with signs.

Pedro Gimeno
  • 218
  • 2
  • 7
1

If you wish to scale along the normals after rotating, simply hit 'x' twice, then enter 3. This will provide you with the dimensions you are looking for/expecting.

In a simple way, think about how the X values of the rotated box are actually smaller than 1 if you apply the 30 degree rotation first. Then once you include the fractions for sine and cosine (M) for the rotation around an origin, the value of X will be even smaller than the simple multiplication by 3. The numbers you are seeing above are correct. But they are the result of a certain order of operations. Use the correct order of operations or scale on the normals for the expected results.

If you search for "scale rotate translate", you'll find videos and articles about this for game programmers.

Updated with calculations because my initial answers were vague:

The Z scale is the result of the 30 degree rotation multiplied by 2:

>>> (sqrt(3)/2) * 2
1.7320508075688772

The X scale is the result of 1) the ratio of the scale to 3:

>>> 2.6459962836960553/3
0.8819987612320185

and 2) the ratio multiplied by sqrt(3)/2 to compensate for the ability to calculate the radius of the increase on the X axis, like so:

>>> (2.6459962836960553/3) * (sqrt(3)/2)
0.7638333333333335

Now with the scale ratio included into the 30 degree calculation, you can see the resulting X value of a vertice on the long end of the scaled and rotated cube:

>>> 3 * ((2.6459962836960553/3) * (sqrt(3)/2))
2.2915000000000005

Which from the other side, means that the scaling factor for X can be arrived at this way:

>>> 0.7638333333333335 * 3
2.2915000000000005
>>> (0.7638333333333335 * 3)/(sqrt(3)/2)
2.6459962836960558

Also, if you are curious about calculating the X and Z values, don't forget to calculate the length from the origin point to the corner of the cube before scaling or rotating.

>>> sqrt((1**2)+(1**2))
1.4142135623730951
  • Yes, I know that hitting x twice will rotate around the local x-axis. This is not what I'm asking about. You write 'use ... for the expected results'. But I didn't expect it to scale along the local x-axis. I just want to know how the numbers 2.646 and 1.732 are calculated. – Daan Michiels Apr 29 '16 at 17:18
  • I attempted to point you in the right direction in paragraphs 2 & 3. – Robert Tyner Apr 30 '16 at 02:26
  • Here's the crux. once you rotate the cube, prior to scaling, you expose the Z axis to further scaling as well, because your vertices(which are really the numbers you should be referencing rather than scale factor) are what are actually getting modified. You might think of the changes to Z as sharing a ratio of requested changes to X. This is why the scale factor for both gets changed, where X is not quite 3 and Z is slightly modified. – Robert Tyner Apr 30 '16 at 02:27
  • I don't think we understand each other. I'm looking for a formula to calculate the numbers 2.646 and 1.732. How is the 'sharing' of the ratio (as you call it) calculated? – Daan Michiels Apr 30 '16 at 02:32
  • Also, the vertex data (the data that is stored inside the mesh) has nothing to do with my question. I'm just talking about transformation matrices. The question applies equally well to empties. – Daan Michiels Apr 30 '16 at 02:38
  • Well, if nothing else 2.646 and 1.732 are simply the distance from the origin to the faces along their respective origins. If you apply a -30 degree rotation and go into edit mode, you'll see the verts (global under 'n') match up. Getting the calculation may involve some trig where 15/30/60 degree offsets affect the scales. – Robert Tyner Apr 30 '16 at 08:39
  • 1
    Yes, 2.646 and 1.732 are those distances, by definition. You write '... may involve some trig...'. It's the trig I'm looking for. – Daan Michiels Apr 30 '16 at 15:10
  • 1
    Please see my amended answer for calculations. – Robert Tyner May 01 '16 at 11:03
  • Does that help? Or have you solved this to your satisfaction? – Robert Tyner May 03 '16 at 21:22
  • I follow your calculations, but I don't see how they explain where the numbers come from. Why sqrt(3)/2*2 for the z-scale? For the x-scale: sure, you can calculate the 2.646 from the 0.7638. But where does the 0.7638 come from? (The only answer I see is 'from the 2.646'...) – Daan Michiels May 04 '16 at 02:35
  • (Btw, I think you meant sqrt(3) instead of sqrt(2) right at the end. But this is unimportant. I'm not interested in coordinates of vertices.) – Daan Michiels May 04 '16 at 02:35
  • sqrt(3)/2 is how Cosine is is calculated for 30°. You can find a set of common values from the unit-circle: https://en.wikipedia.org/wiki/Unit_circle. In this case it's how X values are calculated as they rotate around the origin point(X values were focused on as they gave us the most unpredictable value which was ~2.646). The scale that you are trying to discover is based on the movement of vertices. So ignoring vertices will ultimately be impossible. – Robert Tyner May 04 '16 at 20:42
  • I'll leave the matter alone with this... if you look up matrix math for rotations, you'll find that sine and cosine values are distributed across axis values to perform the rotations. Scale calculations are more simple, being simply a multiplication across the matrices as you mentioned in your question. – Robert Tyner May 04 '16 at 20:42
  • But the reason that this particular scaling wasn't simply multiplication by an axis value in this case, is because the geometry's vertices had multiplication of cosine and sine applied prior to the scale, making the geometry along the axes uneven thus producing a ratio of the scaled axis by 30° (sqrt(3)/2),1/2). – Robert Tyner May 04 '16 at 20:42
  • sqrt(2) is so that you find the distance from the origin point to the corner of a 2x2 cube. That gives you your starting X,Z values prior to rotation, then scaling. --not a typo – Robert Tyner May 04 '16 at 20:46
  • I still don't think you have answered the question. Let's not fill the page with a discussion in the comments, but instead talk in chat: http://chat.stackexchange.com/rooms/39303/how-does-scaling-workin-blender-exactly – Daan Michiels May 04 '16 at 21:33
0

From what I can tell (I was able to get one of your stated values 1.732) the numbers being stored in the matrix to the local X is a projection value to it's local mid-point in world space as seen in the screenshot below. As for your scale value I would hope that would be the 2.732 value instead of your stated illustration value of 2.646, so on that one I'm at a loss.

WorldProjectionToLocalCoordinates

Rick Riggs
  • 4,623
  • 14
  • 30
  • @kheetor - Sorry, I just saw that there were hidden comments to this post, and you had the same idea. I will gladly withdraw my answer if you post it. – Rick Riggs Apr 30 '16 at 12:02
  • Aha, at least we have the value 1.732 showing up somewhere! I'll play around a bit with this idea to see whether I can explain both numbers (the 2.646 is correct). – Daan Michiels Apr 30 '16 at 15:07