[SOLVED] Best way to clear matrix scale?

I have a matrix that’s a combination of multiple matrices, with position, rotation, etc… and I want to reset the scaling of that matrix to 1,1,1, while keeping all the other transformations. Is that possible? What’s the best way to do it?

Thanks!

Just use the Matrix.Scale property

impossible.

Finalmatrix = combination of transformations. If you multiply a base transformation with a scaling factor you cannot read the scaling factor from the final matrix by simply reading values.

If you have a scale matrix in there you need to check that. Otherwise there shouldn’t be a scaling factor afaik.

Use Matrix.Decompose() to get back scale, rotation, & translation.
From there you can either create a new matrix from rotation & scale or an inverse matrix from scale.

1 Like

The scale property just gets/sets the elements on the diagonal, so it will be wrong if you have any rotation. I don’t think we should have this property for this reason. Alternatively we could reimplement it using decompose.

1 Like

I tried doing it but it didn’t work, and the reason (I think) is that the final matrix is made from a tree of matrices, and every node in the tree can apply transformations in different order (eg some nodes first apply translation and then rotation, some first scale, etc. Does it make sense to you or do you think I just made a mistake using it?

Indeed, thanks for the perfect explanation,

Maybe I should explain in details what I’m trying to achieve (assuming @nkast suggestion won’t work), I bet there’s a way to do it but I didn’t provide much data tbh.

Do you know how cameras in Unity engine works? basically they act just like a node somewhere in the scene, which gives you the ability to do stuff like creating a node for the player, add transformation on it, move it around etc, and then add the camera attached to the player’s node (as a child) with a constant offset from it. This creates a camera that will follow the player node and rotate with it.

I’m trying to do something similar, and I got it all working, except for a problem with scaling - if I add the camera to a node with scale, the camera node inherit that scale with the other transformations and it affects the viewport (makes everything bigger / smaller).

So basically I want to enjoy all the benefits of having the camera in my scene graph, but keep its scale constant. Any ideas? :slight_smile:

The view matrix is the inverse of camera’s transform matrix (or WorldMatrix).
So you can have the camera on a scene graph node as any other item/entity, take the inverse of it’s transform matrix and use that as a View Matrix.

In other words, when you use this viewMatrix in the shader, you transform every object into camera’s frame of reference. Imagine that the camera is positioned at (0,0,0) looking forward, because CamTransform * CamTransform.Inverse() = Identity and everything else is positioned in front of it.

@nkast quoting myself:

Like I said I already did the invert thing etc and it works. My question is how to cancel scaling. Thoughts? :slight_smile:

So, if the parent node has let’s say x2 scale, the objects appear smaller, half the size?
Maybe it’s the order you apply the transformations. I can’t really tell.

It’s a good reason imo. XNA 4 doesn’t even have the property.

Yes, and it creates awesome (yet undesired) effects :weary:

Edit: now that I got home and could work on it I played a little more with the Decompose (as you originally suggested), and got it to work. It looks something like this:

            // get inverted transformations matrix - this will be the View matrix of the camera
            Matrix view = Matrix.Invert(worldMatrix);

            // cancel scale
            Vector3 pos; Quaternion rot; Vector3 scale;
            worldMatrix.Decompose(out scale, out rot, out pos);
            view = view * Matrix.CreateScale(scale);

            // update the view matrix of the graphic camera component
            cameraScale = view;

The reason it didn’t work when I first tried it was because I took the scale from the camera matrix, which was inverted, and I needed to get the scale before it…

And now I got Unity-like cameras! :slight_smile:
One downside to it - negative scale still mess things up, but I’m totally ok with it.

Thanks!

1 Like

Transform your position as usual and then use LookAt with the calculated position parameters, why doesn’t that work for you

Plus the scale shouldn’t matter if it applies only to the transformation before the translation

I already got it working, but out of curiosity I tried your method as well:

            // decompose the world matrix
            Vector3 scale; Quaternion rot; Vector3 pos;
            worldMatrix.Decompose(out scale, out rot, out pos);

            // build camera's matrix based on position and rotation of the world matrix
            Matrix view = Matrix.CreateLookAt(pos, pos + Vector3.Transform(Vector3.Forward, rot), Vector3.Up);

            // update the view matrix of the graphic camera component
            CameraView = view;

In general it works, but when I put rotating nodes inside rotating / moving nodes, sometimes it “jumps” instead of going smoothly (not like low FPS / log jumps, like weird calculation errors jump). Not sure why, the math seems correct…

Anyway I think I’ll stick with the method I had above (cancelling the scale), although what I like in your method is the control over the LookUp vector.

Thanks!

Provided you never multiplied the final result by a projection matrix if this is still a world matrix as it seems.
Then If you multiplyed a world by a scaling matrix and just mulitplied it by some rotations and such (again without a projection matrix in the result).

Then you should be able to simply save the inverted scale when you create the scaling matrix itself e.g. if scale = Vector3(x,y,z); to be set to the scaling matrix.
Then the reciprocal of the scaling vector is found by Vector3 iscale = 1f / scale;
So that per component. i = 1f / s;
Were the original values or identity vector 1,1,1 are found by iscale * scale;
Such that iscale can be set to a scaling matrix.

To illustrate in practice were 2f is a component of the scaling vector s
and i is the recipricol 1/s.
1 = i * s;
.5 = 1f / 2f; // .5 is the reciprocal of 2f;
so that
1 = .5 * 2f; // 1 is the numerator (which is always 1 here for us) then the identity scalar is found by the reciprocol (now used as a coefficient) * the original scalar

So that you should be able to reset the scale of your matrix by simply…

m = m * Matrix.CreateScale(iscale.X, iscale.Y , iscale.Z);
or
m = m * Matrix.CreateScale( 1f / scale.X, 1f / scale.Y , 1f / scale.Z);

You might need to pull the translation vector then reset it after you do the above as well depending on your order of operations.

Again provided you haven’t projected as the projection matrix skews the scaling depending on z in that case you will need to first use matrix.UnProject Though you might be able to rig up your own function that sets m41 thru m43 to descale this wouldn’t be straight forward to do it.

CreateWorld and CreateLookAt if i remember right basically set the scale to 1 by normalizing. Which you should also be able to do (though im not sure about this one) on M.Forward.Normalize(), M.Up.Normalize() M.Right.Normalize() to bring the matrix into line with a scaling factor of 1.

1 Like

@willmotil Thank you for the additional suggestions (and info!) but I already found a working fix I’m happy with.
Cheers! :slight_smile: