How can I rotate about an arbitrary point in 3D (instead of the origin)?

I have some models that I want to rotate using quaternions in the normal manner, except instead of rotation about the origin, I want it to be offset slightly. I know that you don't say, in 3d space, that you rotate about a point; you say you rotate about an axis. So I'm visualizing it as rotating about a vector whose tail is positioned not at the local origin.

All affine transformations in my rendering / physics engine are stored using SQT (scale, quaternion, translation; an idea borrowed from the book Game Engine Architecture.) So I build a matrix each frame from these components and pass it to the vertex shader. In this system, translation is applied, then scale, then rotation.

In one specific case, I need to translate an object in world space, scale it, and rotate it about a vertex not centered at the object's local origin.

Question: Given the constraints of my current system described above, how can I achieve a local rotation centered about a point other than the origin? Automatic upvote to anyone who can describe how to do this using only matrices as well :)

• Quaternions already describe a rotation about an arbitrary axis; do you have problems constructing such a quaternion from the data you have? – Martin Sojka Nov 2 '11 at 9:30
• Seriously, could the people who upvote answers actually read them? I gave a method, an efficient formula, and even a demonstration. Yet the only upvoted answer, while providing some valuable information (and also some plainly wrong information), features none of these and does not even answer the question! – sam hocevar Nov 2 '11 at 9:51
• @MartinSojka, this is about and arbitrary point, not an arbitrary axis. – notlesh Nov 2 '11 at 18:53
• @SamHocevar Both your answers were helpful. I chose yours because it was more thorough and helped me arrive at a solution. Thank you both. – notlesh Nov 2 '11 at 18:54
• Ah, sorry - I had it confused with Dual Quaternions (those get you the translation "for free" as well). I'll write up what I meant in an answer later; maybe others would find it to be useful, especially since you can reduce your three components to just one - albeit a bit more complex one. – Martin Sojka Nov 2 '11 at 22:40

In short

You only need to change T in your SQT form.

Replace the translation vector v with v' = v-invscale(p-invrotate(p)) where v is the initial translation vector, p is the point around which you want the rotation to occur, and invrotate and invscale are the inverses of your rotation and scale.

Quick demonstration

Let p be the point around which you apply rotation r. Let s be your scaling parameters and v your translation vector. The final matrix transformation is T(p)R(r)T(-p)S(s)T(v) instead of R(r)S(s)T(v).

What you want is new transformation parameters v', r' and s' such that the final matrix transformation is R(r')S(s')T(v') and we have:

T(p)R(r)T(-p)S(s)T(v) = R(r')S(s')T(v')


Behaviour at infinity indicates that rotation parameters and scaling parameters cannot change (this could be demonstrated). We thus have r = r' and s = s'. The only missing parameter is therefore v', your new translation vector:

T(p)R(r)T(-p)S(s)T(v) = R(r)S(s)T(v')


If these matrices are equal, their inverses are equal:

T(-v)S(-s)T(p)R(-r)T(-p) = T(-v')S(-s)R(-r)


This especially holds true for origin O:

T(-v)S(-s)T(p)R(-r)T(-p)O = T(-v')S(-s)R(-r)O


Scaling and rotating the origin yields the origin, whe thus get:

T(-v)S(-s)T(p)R(-r)(-p) = -v'


v' is the new translation vector you are looking for that lets you store your transformation in SQT form. It is probably possible to simplify the computation; but at least the required storage is not increased.

• Thanks for the explanation. BTW, do you know of any resources where I could read more about SQT representation tricks? – pachanga May 5 '13 at 4:18
• Correct me if I'm wrong but it seems another solution would be to store your Quaternion as normal, and if you need to account for translation around an arbitrary point/axis, then build the Q matrix with this included, simply extract the translation vector from this matrix (last column, usually) and add it to the objects Translation vector, then toss out your temporary matrix. – johnbakers Jun 7 '13 at 1:04

All of the canonical rotational formulas used to derive your rotation matrices are for rotation about the origin. If you would like instead to apply that rotation around a specific point, you must first offset the origin -- or, equivalently, move the object so the point you want to rotate about is at the origin.

Consider the 2D case first, because it is simpler and the technique scales. If you had a cube of width 2 centered on the origin and you wanted to rotate it 45 degrees about its center, that would be a trivial application of the 2D rotation matrix.

But if instead you wanted to rotate it around it's upper right corner (located at 1,1) you'd first have to translate it so that corner was at the origin. This can be accomplished with a translation of -1,-1. Then you can rotate the object as before, but you must follow this up by translating it back (by 1,1). So in general, to achieve the rotation matrix R for a rotation of r about point P you do:

R = translate(-P) * rotate(r) * translate(P)


where translate and rotate are the canonical translation/rotation matrices, respectively. As it happens, this scales trivially to 3D, which the exception of having to supply an axis to the rotation as well -- you could just always choose the canonical X, Y or Z axis rotation matrices, but that would be dull. You'll want to use the arbitrary axis-angle rotation matrix. Your final R in 3D is thus:

R = translate(-P) * rotate(a,r) * translate(P)


where a is a unit vector representing the axis of rotation and P is now a 3D point in model space representing the rotation point.

As it happens, quaternions can be converted to and from matrix representations, so you could do your concatenation that way should you so choose. Or you could just leave everything as matrices (quaternions have some nice advantages such as being easier to interpolate in a sane fashion, but whether or not you need that is up to you).

Also:

So I'm visualizing it as rotating about a vector whose tail is positioned not at the local origin.

Strictly speaking, while vectors can be used to represent positions by considering them as displacements from an origin, vector's don't have positions themselves so it's a bit unusual to visualize one as such.

• Thanks, this is a good reply. It doesn't fit the constraints of my system though. I should have included in my question, "is it possible to do this given these constraints?," and I think the answer is that it is not, since this requires two translations and I only provide for one. Is this an inevitable shortcoming of the use of SQT as a representation of affine transformations? – notlesh Nov 1 '11 at 18:42
• It fits perfectly within your constraints. The matrix R (produced as translate-rotate-translate-back) is your rotation matrix. Replace Q with R in your "SQT" system so you have the more common scale-rotate-translate paradigm, and you're done. That last translate is independent of the two intermediate translations done to produce the desired rotation. – Josh Nov 1 '11 at 19:27
• You're proposing I replace the quaternion with a matrix? That's 12 more bytes per object (8 if I store it as a 4x3 matrix)! I'll silence the optimist in me, though, and give this a whirl. (That actually probably won't even amount to a 2kb increase in footprint...) Thanks for your responses. – notlesh Nov 1 '11 at 20:00
• You could -- you could also convert between them, constructing the rotation quaternion that way and plugging in back in to your existing system. – Josh Nov 1 '11 at 21:25
• @SamHocevar: Alternatively, any combination thereof can be expressed as a single screw. – Martin Sojka Nov 2 '11 at 16:38