Sign in to follow this  
Kwizatz

Adding or Concatenating Transforms (Scale, Rotation, Translation)

Recommended Posts

Kwizatz    1392

I keep my transformations as a scale vector, a rotation quaternion and a translation vector (SRT) rather than a single matrix, when I need a matrix I do some conversion and get a 4x4 matrix out of the 2 vectors and quaternion.

 

To add or concatenate 2 transforms what I do is generate the matrices from the 2 SRT transforms and multiply them together, which is fine since I rarely require to convert back from Matrix to SRT.

 

In the long run I think there are some matrix multiplications that can be shaved off if I could "add transforms", so I am trying to do that, but I ran into some issues.

 

My basic approach is as follows:

 

I have 2 SRT structures which I want to concatenate.

 

I do a element wise multiplication of the scale vector (S = S1[0] * S2[0],S1[1] * S2[1],S1[2] * S2[2]), this seems to work, but for now I have no scaling, so all values are 1, I just need to know if this is correct or if there is something wrong there.

 

I do a simple quaternion multiplication with R1 * R2, again this works as expected as the 3x3 sub-matrix is the same as when I convert to matrix and multiply matrices.

 

I to an element wise addition of the translation vectors so T = T1[0]+T2[0],T1[1]+T2[1],T1[2]+T2[2], and this is where it all goes wrong, the values on the matrices are different, and now that I think of it, this may have to do with the last element in the matrix, the 4th element of a position vector....

 

TL;DR version:

 

So anyway, long story short I want to concatenate/add transforms in Scale, Rotation, Translation format and then convert the result to a 4x4 matrix rather than convert the SRT's to matrices and then multiply the matrices, but the translation vector addition is giving me trouble.

 

Any ideas?

 

Thanks in advance!

Share this post


Link to post
Share on other sites
You don't add translations when they are 4x4 homogeneous matrices, you (matrix) multiply them, that's the whole point of homogeneous matrices (all affine transforms can be multiplied). Just make sure the rest of the translation matrix is the identity (i.e. the upper 3x3 part). Edited by Paradigm Shifter

Share this post


Link to post
Share on other sites
Kwizatz    1392

Hey, thanks! that makes perfect sense, I though of something like that, but for some reason I kept thinking the 3x3 upper part had to already be populated with rotation and translation data instead of the identity... I am glad I posted. smile.png

 

Would scale work the way I am doing it though? I would like confirmation on that.

 

Thanks again!

Share this post


Link to post
Share on other sites
Yeah, scale works the same way, just make sure the 4th row and column is (0 0 0 1). (So it's diagonal with zeros everywhere except leading diagonal which is (scalex scaley scalez 1).

EDIT: There is always a 1 in the bottom right corner of homogeneous transform matrices. Edited by Paradigm Shifter

Share this post


Link to post
Share on other sites
Kwizatz    1392

Hmmmm no, that didn't work, I get the same values.

With identity matrices for the SR part the T part becomes the sum I am already doing, but inverted (IE: T2 + T1 instead of T1 + T2).

The bottom right value does remain as 1, so no real change.

Edited by Kwizatz

Share this post


Link to post
Share on other sites
HappyCoder    5052
You will only be able to keep the pieces of the transformation separate if you only allow uniform scaling. This is because by appending transforms that don't enforce uniform scaling you can end up with a matrix that skews vertices. Being that you cannot skew vertices with a scale, rotation, then translation. You will not be able to concatenate transformations and keep them in the split representation.

Share this post


Link to post
Share on other sites
yckx    1298

I don't think this is going to work. IIRC, if you have a transform matrix that has scaling and/or rotation info as well as translation, the scale and rotation are applied first. So the translation components can't be combined independently, unless the scale and rotation of both SRTs are identical. Matrix multiplication isn't commutative, so you can't really expect to get an accurate result if you change the order inl which you combine the component transforms.

Share this post


Link to post
Share on other sites
Kwizatz    1392
I don't think this is going to work. IIRC, if you have a transform matrix that has scaling and/or rotation info as well as translation, the scale and rotation are applied first. So the translation components can't be combined independently, unless the scale and rotation of both SRTs are identical. Matrix multiplication isn't commutative, so you can't really expect to get an accurate result if you change the order inl which you combine the component transforms.

 

Yeah, you're right, I found out that if I apply the rotation of the first SRT to the translation of the second SRT before adding the 2 vectors, I do get the same vector as the matrix multiplications, give or take 0.000001 due to floating point error (scale is 1,1,1 so its mute at the moment, but I am sure I would have to take it into account as well).

 

My "rotate_vector_by_quaternion" packs quite a lot of operations, so now I am just pondering whether its worth it or not. In the end what I may be saving is a temp array of matrices since what OpenGL (and I suspect Direct3D) expects is matrices as well anyway.

 

Thanks for your help!

Edited by Kwizatz

Share this post


Link to post
Share on other sites
yckx    1298

I wouldn't worry too much about trying to devise a more efficient way to concatenate transforms unless you've determined empirically through profiling that this bit of code is your bottleneck. Your first priority should be code that you can read and understand after not looking at it for six months. Multiplying matrices is a standard approach to transform concatenation, so let it be your first choice. Otherwise you run the risk of wondering what the hell you were trying to do, how it was supposed to work, and why you were doing it in the first place.

Share this post


Link to post
Share on other sites
EduardoMoura    298

Yckx you just resumed my coding life!

 

I have been looking for a piece of code I wrote about 8 years ago, and I am still trying to figure out what the hell was I thinking when doing that.

Don't do the same mistakes, if you do change, comment through fully, you never know when you will need to revisit your old code. And believe me, sometimes when I am really sleepy or tired I code extraordinary things that work, do what is supposed, but I have no idea why. 

 

So unless this really is your bottleneck (which I doubt), don't really bother trying to optimize the code.

Share this post


Link to post
Share on other sites
Kwizatz    1392

You're right, and I agree, my matrix code works, but to give some more context to this, I'll explain how this idea came to be.

 

My skeleton file format stores transforms as SRT, as I explained, my animation file format stores a "channel" per vector element, so animations are applied to the SRT vectors as well, this works great, BUT at runtime I need to generate a temp array of matrices (that is, an array of arrays) for the bind pose skeleton pose, a temp array of matrices for the animated joints which gets multiplied by the bind pose array and a final array for the result of the previous multiplication.

 

I cache the bind pose array since it never changes, but keeping duplicate data on memory kind of bothers me, and having a temp array with the SRT's converted to matrices provokes the same nagging annoyance in me.

 

So I though that if I could do the operations with the SRT's directly I could do without the auxiliary arrays, so no duplication of data in memory, and at the time avoid a runtime pre-computation (I am trying to do all preprocessing offline so my file formats are ready to use as soon as they're loaded into memory and some pointers are set), and some matrix multiplications here and there.

 

To me, it is an interesting topic, I have not dumped my matrix code yet, and I won't, but I'd like to know what could I get away with.

Share this post


Link to post
Share on other sites
RobTheBloke    2553

You can do everything with SRT (and upload them to the GPU and process the verts with them directly if you wish). Doing so has a number of problems. Firstly, SRT * Vertex is an exceptionally slow operation (well, it's not that bad, but it is much slower than matrix * Vertex). Secondly, SRT * SRT is also fairly slow when compared to matrix * matrix. Really the only time SRT makes any practical sense, is when you are performing lots of interpolations (which is quicker with SRT than with matrices). Most people store the bind pose as matrices because, well, it's efficient as is possible (assuming you have SIMD optimised your matrix operations. If you haven't, then you *may* be able to squeeze a tiny advantage with SRT).

Share this post


Link to post
Share on other sites
Kwizatz    1392
In the end I did manage to get it working, I am doing this for animation interpolation, so it does seem justified. I convert to matrix before feeding the transforms to the gpu.
I do think this way is more efficient at least as long as it stays on the CPU, many multiplications can be avoided for example because one of the multiplicands being zero for the translation part and the scale part (what I did was convert the vectors to matrices and assume constants for the non variable elements in the matrix, then simplify the matrix multiplications),
the most expensive operation then becomes rotating the translation vector using the quaternion, since it implies a quaternion to 3x3 matrix conversion and a matrix vector multiplication but still not as many operations as the regular matrix multiplication.
You only need to apply rotation and scale to the translation, I think, because of the nature of translation, a skew in 4D, rather than being a linear transform in 3D, and in any case it makes sense when you think of the translation vector as being in the space of the rotated and scaled axes.

I am leaving SIMD viability as an exercise for later.

Share this post


Link to post
Share on other sites
Dmytry    1151

You could have a transformation that rotates by 45 degrees, then scales the x axis by a half, then rotates back. This transformation can not be represented with scaling vector, it happens along an axis that is at 45 degrees.

 

For the rotation and translation, your transform is like

 

V'=rotation*V+translation

 

and for two transforms, it is

 

V'=rotation2*(rotation1*V+translation1)+translation2

or

V'=rotation2*rotation1*V + rotation2 * translation1 + translation2

so you multiply your rotations and you add translations after multiplying the earlier translation by the later rotation.

 

The multiplication by rotation here means rotating by a quaternion, i.e. represents Q*a*~Q where ~ is inverse.

Share this post


Link to post
Share on other sites
Kwizatz    1392

Hi Dmytry,

 

I am not sure I follow, what do you mean? in the end what my SRT operations do is a simplified matrix multiplication, so if you take S,R,and T as matrices with scale, rotation and translation respectively, and I as identity the operation you describe would be something along these lines:

 

( I * R * I ) * (S * I * I) * ( I * *R * I ),

 

which is the same as RSR, wouldn't the scale vector (diagonal) on the second matrix be doing the scale on world axes rather than object axes as well?

I think I see where you're going, but I haven't had time to think more about it.

 

The second part of your post, I am not sure if you're pointing out a (another?) problem or providing me with an optimization smile.png

 

Thank you for your comments!

Share this post


Link to post
Share on other sites
RobTheBloke    2553

He seems to be saying that you should always use matrices because RSR cannot be represented by SRT, which is simply not true. SRT * SRT * SRT can represent RSR just fine, not to mention the fact that your DCC package is going to be authoring data in SRT anyway, so why bother worrying about an edge case that will never happen?

Share this post


Link to post
Share on other sites
Kwizatz    1392

I see, that's what I though, I am still in need of some time to reassess non uniform scaling, but with uniform scaling it seems fine to just multiply the scaling factors, since scale would be the same in all directions.

 

And yes, this is going to handle mostly DCC'ed meshes and animations, the 'optimization' in fact came about because of how I was doing animation interpolation.

 

Thanks again!

Share this post


Link to post
Share on other sites
alvaro    21246
Just drop non-uniform scaling: The notion of what constitutes a non-uniform scaling is not an intrinsic feature of the transform (in other words, it depends on the coordinate system used), which is the root of a lot of problems. I don't know what people use non-uniform scalings for anyway.

Share this post


Link to post
Share on other sites
RobTheBloke    2553
If you want to take account of non-uniform scaling, you can simply do the following:

transform = [S] * [R] * [IS] * [T]

Where IS is the inverse parent scale, which is pretty much all that most dcc packages do....

Certainly maya applies that transformation for all joint nodes, but geometry leaf transforms usually just use a plain SRT.

I don't know what people use non-uniform scalings for anyway.

Speak to an animator, and they usually find a million reasons to support it. A common trick is to apply non-uniform scale on leaf transforms within a skinned mesh to prevent collapsing elbows, or to simulate the effect of muscles bulging. I've even seen rigs that have made use of excessive amounts of animated scaling for classic-cartoon-like squash and stretch. It does have its uses, which is why animators make so much use of it in maya et-al. A seasoned animator in the games industry may not use it at all, but that's usually due to them having been pre-conditioned to not use scaling on pain of death, not because it has no use. Edited by RobTheBloke

Share this post


Link to post
Share on other sites
alvaro    21246
I bet animators could also make use of shearing transformations, which just means that they should be given access to general affine transformations, not just movements (rotation + translation) or a combination of movements and uniform scalings. The only problem with that is what to do about interpolations.

Share this post


Link to post
Share on other sites
Kwizatz    1392

I am considering dropping non uniform scale support, if I do, I downsize my SRT structure from 10 floats to 8, chances are scale won't even be used at all, but the reason why I added it on the first place was because of a model that used mirror scaling on X to duplicate the piece of armor from the right to the left. Granted, I could modify, and fix the model, but, but there were thousand of these shortcuts taken by the modelers, I'd have a problem. I am using stock models so its not like I could go to the modeler and ask him/her to fix it and submit it again.

Share this post


Link to post
Share on other sites
Dmytry    1151

Hi Dmytry,

I am not sure I follow, what do you mean? in the end what my SRT operations do is a simplified matrix multiplication, so if you take S,R,and T as matrices with scale, rotation and translation respectively, and I as identity the operation you describe would be something along these lines:

( I * R * I ) * (S * I * I) * ( I * *R * I ),

which is the same as RSR, wouldn't the scale vector (diagonal) on the second matrix be doing the scale on world axes rather than object axes as well?
I think I see where you're going, but I haven't had time to think more about it.

The second part of your post, I am not sure if you're pointing out a (another?) problem or providing me with an optimization smile.png

Thank you for your comments!

Not really pointing out an error in the first part, just explaining that what you originally wanted to do - concatenate sequence of SRT into a single SRT where S scales using a vector that represents scaling on different axes, is (technically speaking) impossible - you indeed need a full blown matrix here, or if you want to optimize a tiny bit, 3x3 matrix and translation. That's because sequence of R S R can produce scaling along the diagonal, which you can not represent with one S R T . (I am assuming matrices apply in left to right, i.e. directx order)

edit:However if you are certain you only use non uniform scaling as the first transformation applied, then it can work. If U is uniform scaling and you only have S R T U R T U R T U R T .... sequence, then you're in the clear.

Second is indeed providing with an optimization. In my software I have a class that does rotation using quaternion and translation using a vector, it works pretty much same from outside as a matrix. The reason I'm using that is not so much optimization as convenience when doing physics or especially when interpolating rotations between frames (which I do because I run physics at constant framerate). In your case I'd probably just convert to matrices then multiply those together, I'd probably use 3x3 matrices with translation rather than full blown 4x4 , basically, you assume the right column (or bottom row, depending to convention) is always 0 0 0 1 . Edited by Dmytry

Share this post


Link to post
Share on other sites
Kwizatz    1392

Alright, thanks Dmytry, I am just using these for DCC animation interpolation, skeletal animation to be exact at the moment, so I think Rob is right on the money, I still need to go back and factor in scaling properly, currently busy with my resource system.

 

Thanks to all again!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this