# 3D vector rotations around an axis.

## Recommended Posts

Hey everyone, k I've encountered a huge road block. My code is a mess with jumbled bits of different math types and what I want to achieve is turning out to be a lot harder than I thought. Does anyone know a great site that teaches how to obtain an axis for 3d point rotations of vectors. What I really want is to rotate 3d vectors around an axis specified by a line. So far I have been rotating 3d vectors around 3d 'lines' so to say that are fixed to a central point of 0,0,0. In my head I say to myself, &quot;k that's the perpendicular line&quot; or &quot;I want a perpendicular axis from that line relative to some arbitrary up direction&quot; BAHHH! Thanks

##### Share on other sites
Your language is kind of confusing. Of course, this is the kind of math where using the correct language is just as hard as solving the problem.

So let's make things as clear as possible. What information do you have? This should look something like "a center of rotation (which is a 3d point), an axis of rotation (which is a 3d vector) and an angle (in radians)." From that, I can give you code that will compute a rotation that does the job, and then code that can apply that rotation to any other point. I can even explain how everything works. But first I need to understand exactly what you need.

##### Share on other sites

I think the following is meant. Perhaps not a totally correct explanation though...

Applying a non-identity rotation maps a couple of points onto themselves. These points build a line which is often called the axis of the rotation. Further, a rotation maps the point zero onto zero due to its multiplicative nature. In other words, the axis of rotation ever passes through zero. If this should not be the case, you need to temporarily shift the space so that the axis correctly passes through zero, apply the rotation, and undo the shift.

I.e. with p being a point where the axis should pass through, then  Tp ) * R * T( -p )  would do the trick, where T denotes a translation matrix and R the rotation matrix.

Edited by haegarr

##### Share on other sites

My understanding was similar to what Haegarr described (which might not actually be the problem). Every time I rotate it would be about some line (an axis) that goes through the origin so it makes perfect sense to me that that's what your rotations do. If that isn't what you want then you need to do as Haegarr suggestions. It might help if you post a little code where you actually use your rotations, that might better show the limitations of your current solution.

What language are you using, what do you use for your visuals etc? There are a few very solid libraries out there that might be worth checking out.

##### Share on other sites

o.k... I'm using THREE.js working with webGL

Here are two sample pictures to illustrate what I'm trying to achieve.

[attachment=34326:Screenshot from 2016-12-28 10-38-13.png]

if you look closely at the picture above you'll see a faint pentagon drawn along the contour of the world with small arrows pointing in various right angle directions

[attachment=34327:Screenshot from 2016-12-28 10-38-49.png]

in this above screen shot you'll see a pentagon that is drawn according to some arbitrary plane indicated by the big red arrow, more about this one in a second.

[attachment=34328:Screenshot from 2016-12-28 10-42-11.png]

This screen shot simplifies the explanation of what I'm trying to do.

K, first let me explain what I mean by each term I'm using because I'm not on the same page as you guys.

Plane: I use the term plane to indicate the normal of a plane.  A plane being a flat surface and then the normal being projected perpendicular to that surface.

right angle to a Plane: in the third picture you can see that I have a red arrow which is at right angles to the blue arrow, though it may not look like it at that particular angle in the picture.

axis: o.k admittedly I use axis interchangeably with plane.

So, currently what I do to derive the blue and red arrows is as follows

// this is all within a class

this.normal = this.position.clone().normalize();
this.axis = $m.v(0,1,0); //$m.v() constructs a basic vector, slightly modified version of THREE.Vector3().
this.perpendicular = $m.rV($m.A_R( 90 ) , 1 , this.normal , this.axis , false );  //$m.rV rotates stuff according around a fixed point x:0, y:0, z:0 this.forward = target ||$m.rV( \$m.A_R( -45 ) , d , this.normal , this.perpendicular , true );

var g = new Ogeometry( this.scale ); // Ogeometry is a basic geometry with vectors of [1,0,0] , [0,1,0], and [0,0,1].
g.mesh.position = this.position.clone();
g.mesh.up = this.normal.clone();
g.mesh.lookAt( this.forward.clone() );
g.mesh.updateMatrixWorld(); // THREE.js does some stuff here that I don't understand, never looked at the code though i'd like to know.

this.up = g.mesh.geometry.vertices[0];
this.right = g.mesh.geometry.vertices[1];
this.front = g.mesh.geometry.vertices[2];

this.up.applyMatrix4( g.mesh.matrixWorld ); // again THREE.js does some stuff here I don't understand.
this.right.applyMatrix4( g.mesh.matrixWorld );
this.front.applyMatrix4( g.mesh.matrixWorld );

// the result of all of the stuff I don't understand is the blue arrow points from this.position to this.up
// and the red arrow points from this.position to this.front.



What could help me remove so much of my archaic code is if I knew how to obtain a right angle to Plane vector with only the plane being know and the normal of the position being known.  because a right angle to Plane vector point would exist anywhere along a circle encircling the plane.  So the normal of the position would indicate the relative 'UP' direction on the surface of the world and would pinpoint a specific vector along the circle.

##### Share on other sites

That explains your problem a lot better but it's still a little confusing. Are you hoping to tile that planet with pentagons? Should all the red arrows point out from the centre of the planet?

I hesitate to say more in case I am totally off about your problem but maybe this will help somehow. To get a normal that points directly out from the planet at any point is just:

normal = normalize(vertexPosition - planetCentre).

Now you want another vector at right angle to that points around the planet (longitudinal - around it's axis of rotation if I have that correct, I get them mixed up). To do that you just need to cross your new surface normal with the vector which represents your planets axis (which is presumable just global 'up'). I'm going to call this the tangent

tangent = CrossProduct(planetAxis, normal); // the order of those two vectors might need to be flipped.

Finally you need one more vector which points up/down the planet's surface (latitudinal direction if I recall), we'll call this the binormal

binormal = CrossProduct(tangent, normal).

I often get mixed up about which vector should go first in cross products so they may need to be flipped around until they look correct. You should now have 3 basis vectors, one points out from the planet's surface, one points right at a tangent along the planet's surface and the other points up at a tangent along the planet's surface. This should work and be consistent around your planet and can be used to orient those pentagons correctly. As with all this though there is one exception and that is at the poles because the normal and the planet's axis there will be parallel.

That probably isn't what your actual problem is but it sounds similar. For other similar problems you could look up normal mapping as this sorta things is needed there. I haven't read this but a quick search gave this and the images look promising:

http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/

##### Share on other sites
So if you have two vectors, and you wish to rotate one around another, the math turns out to be quite intuitive (see Rodrigues rotation).

The idea is to reduce the problem from 3 dimensions to 2 dimensions, perform your rotation and extend back into 3 dimension. In order to do so, you must take the vector you wish to rotate and break it down into 2 vectors relative to the axis vector - projection vector and a perpendicular vector.

The projection vector is simple, it is the axis vector times the dot product between the axis and point.

v_proj = ( axis dot v )/length(axis) * axis

Subtracting v_proj from v and we get the vector component perpendicular to the axis.

v_perp = v - v_proj

Now, if we treat normalize(v_perp) as the x axis, and normalize(axis cross v_perp) as the y axis, then our v_perp_rotated is:

x_axis = normalize(v_perp)
y_axis = normalize(axis cross v_perp)

v_perp_rot = ( cos(theta) * x_axis + sin(theta) * y_axis ) * length(v_perp)

Our new rotated vector, v_new now becomes

v_new = v_perp_rot + v_proj

NOTE however, this has not been optimized, it is the intuitive explanation for Rodrigues rotation. As it turns out, you can combine some of the operations to skip the normalization and re-lengthening. I encourage you to follow the math! If not, but the gist of it is:

x_axis * cos(theta) * length(v_perp) is merely: cos(theta) * v_perp

y_axis * sin(theta)* length(v_perp) is merely: sin(theta)/length(axis) * axis cross v_perp (and, axis cross v_perp is the same as axis cross v)

##### Share on other sites

I'm going to take a stab in the dark, and guess that the situation is that you have an arbitrary vector in space and you want to derive two perpendicular vectors from it?

In that case, you're going to have to choose an axis that is 'most expendable', in that, you're either going to completely avoid and ignore cases where your arbitrary normal is facing along that axis or you're going to write special-case code to handle anything that's within a threshold (aka 'epsilon') of that axis.

It looks like you're already doing this, in a roundabout way. The vector that you have, which is the 'arbitrary normal', will first be used to perform a cross product with a cardinal vector (like '1 0 0', '0 1 0', or '0 0 1', depending on which vector you want to 'avoid the most'). In the case of '0 1 0', which it appears as if your code is already dealing with, and happens to be the 'vertical' vector in most gfx cases, the cross product of the vector and '0 1 0' will give you a vector completely perpendicular to whatever plane is formed by the vector and '0 1 0'.

If you can imagine a vector pointing in any random direction (edit: the 'arbitrary normal vector' you already have), and a vector pointing straight up, this forms two sides of a triangle if they are emanating from one point. The direction that triangle is facing is the result of their cross product. From here you would generate the 3rd vector by simply getting the cross product of your original arbitrary vector and the resulting vector of the first cross product. The 'cardinal' vector that you use in the first cross product is just a place-holder, and only has a bearing on your final outcome if your math precision is crappy and the arbitrary vector is very close to the cardinal vector itself... (edit: because it would form a very skinny triangle that is hard to calculate the normal vector of precisely)

This means that, in the case of using '0 1 0' as your cardinal vector, any arbitrary vector that is very close to it, or its inverse ('0 -1 0') will start to cause precision issues, but would otherwise be fine for all other vectors where the X and Z values are dominant in the starting vector.

This simple two-step algorithm will yield two vectors that are the 'horizontal' and 'vertical' vectors oriented with your initial arbitrary vector, depending on what cardinal vector you decide to use ('1 0 0', '0 1 0', or '0 0 1').. Typically, the vertical vector is used, because most applications involve 'things' that are facing in more horizontal directions than vertical.

If I'm way off base and clearly have no idea what you're talking about, please let me know. Otherwise, I hope this helps.

P.S. Don't forget to normalize the result of each cross product!

Edited by deftware

##### Share on other sites

That explains your problem a lot better but it's still a little confusing. Are you hoping to tile that planet with pentagons? Should all the red arrows point out from the centre of the planet?

Hi, thanks for the response.  I'll explain what I'm going for in more detail a little further down my post.

So if you have two vectors, and you wish to rotate one around another, the math turns out to be quite intuitive (see Rodrigues rotation).

NOTE however, this has not been optimized, it is the intuitive explanation for Rodrigues rotation. As it turns out, you can combine some of the operations to skip the normalization and re-lengthening. I encourage you to follow the math! If not, but the gist of it is:

Years ago when I wanted to rotate my camera around the sphere freely, not in a fixed longitude latitude style, I came across Rodrigues' formula and modified it to suit my needs.  But you got me thinking as you seem to understand math better than I do and simplified many of the step in computation to achieve the same desired result.  Makes me think I may need to ask you more question in the future. :)

I'm going to take a stab in the dark, and guess that the situation is that you have an arbitrary vector in space and you want to derive two perpendicular vectors from it?

Yes this is much closer to what I want.  Thanks for the explanation.  You mentioned some terminology that I will have to become more familiar with, it would help if I wrote using familiar terminology that is main stream.

O.k, so I have to admit that my question was actual an amalgamation of two problems rolled into one.  I didn't realise this until I solved one of the problems.  I can now explain this problem fully, and judging by what you all know this will be a sinch.

[attachment=34360:Screenshot from 2016-12-28 10-42-11.png]

Using the screen shot from above as reference.

first a little explanation about THREE.js

I will color code which will correspond with the arrows.

in three js when you introduce a geometry into the scene it needs a global 'position' vector (where the red and blue arrows meet), a normalized 'UP' vector and it needs a global 'lookAt' vector.   Then the encoded vertices and faces of said geometry will orientate themselves in the scene as one intends.

Now in the case of a geometry just being a box, if the UP vector and a normalized version of the lookAt vector are not perpendicular to one another, the lookAt vector will determine the true UP vector, or at least this is what I think is going on.

1. I want to be able to pick any point on the sphere and from that point achieve one of two things. Either I will know the normalized UP vector (I want it to be a random vector) and global position vector (which will also be random) and I need to compute a global lookAt vector which preserves the original UP vector

2. Or I know the random position and a selected lookAt vector and need to compute the normalized UP vector according to a normalized position vector.

Now number 2 I already have lots of clunky code shown above and am able to achieve my desired results, after doing everything I wrote above, and the following

crns[0].updatePlane( crns[0].up.clone().sub( crns[0].position ).normalize() );


Now if someone knows how to turn my Chevy code into Ferrari code  for problem number 2 it would be much appreciated.  Oh and a solution for problem number 1.

Edited by Awoken

##### Share on other sites
Okay, I think I can help with number 1 for now.

So it turns out, vector from the center of the sphere to a point on the sphere is your lookAt vector, but in reverse. So, you are essentially on the surface of a sphere trying to look at its center, but you do not know how to extract the up and right vectors to create your view matrix? (from what I can glean, correct me if I'm wrong...I haven't really had the time or chance to read all the way through).

Assuming this is the case, the task can be fairly simple if you are looking for a quick and dirty approach. Let's assume <0,0,1> is The verticals axis in world coordinates. Let's also assume then, it is also your UP axis. "But they aren't perpendicular!" Is what you are thinking l, and you are correct. So, let's make them perpendicular:

If you take the cross product of the lookAt vector and your fake UP vector, you will get your RIGHT vector, perpendicular to both. Now, take the cross product of your RIGHT vector and your lookAt vector and voila! You have a new UP vector perpendicular to your lookAt vector.

Of course, this only works if York lookAt vector is not 90 degrees down or up (aligned with the world UP axis). The cross product will result in a zero , because the math doesn't know where your RIGHT is. In this case, you can either default your UP vector to either your x or y axis, or use the last known RIGHT axis to extract your new UP (and if required, new RIGHT axis because remember, the new lookAt vector is no longer perpendicular to the old RIGHT vector).

Is this a long the lines of what you are looking for regarding your question no. 1?

##### Share on other sites

Is this a long the lines of what you are looking for regarding your question no. 1?

hmmmm.  maybe.  Maybe it is.  So at the moment my lookAt vector is a global point, not a normalized one.  the Up vector is a normalized one.  So are you thinking take a copy of the lookAt vector, normalize it then cross it with the normalized global position and I'll get a true Up vector?

##### Share on other sites

Is this a long the lines of what you are looking for regarding your question no. 1?

hmmmm.  maybe.  Maybe it is.  So at the moment my lookAt vector is a global point, not a normalized one.  the Up vector is a normalized one.  So are you thinking take a copy of the lookAt vector, normalize it then cross it with the normalized global position and I'll get a true Up vector?

Almost, the cross will return a Right vector, that you then cross again with the lookAt vector to get the trueUp vector. That's my thinking, so let's try it.

First, regarding your lookAt vector: to create a vector going from 3D point a to 3D point b, subtract b from a and normalize. So, your lookAt vector would be

vector3 lookAt( vector3 eye, vector3 target ){
return normalize( target - eye );
}

As a simple and intuitive example (I live by these, always simplify!) if the target is located at <10, 0, 0> and the eye is located at <3, 0, 0>, then your lookAt vector will be the vector <10-3, 0, 0>, only normalized, so < +1, 0, 0>, which makes sense.

Now, if you take the cross product between the Right vector and the lookAt vector (or the reverse order depending on your coordinate system), you will get the Up vector, the vector which is the normal to the plane formed by the two vectors, lookAt and Right. And, as long as all 3 are perpendicular to each other, you are good. Simple example- x_axis cross z_axis = y_axis.

Understand however, if the vertical component of your Right vector is not zero, then your view will be tilted (this is called roll, or bank). Since the majority of us look at objects in an upright fashion (don't tilt our heads) I suspect that is something you want too keep. So, what ever your lookAt vector is (as long as it's not straight up or straight down), when ever you cross it with the straightUp vector <0,1,0> you get a vector that is perpendicular to your lookAt vector, and will have a 0 for the vertical component. Now, it won't be normalized, even if the lookAt vector and the straightUp vector are, rather its size will be the sin(theta). So, normalize it.

We now have two perpendicular vectors, take the cross of these to to get trueUp vector. So, two cross products and don't forget to normalize! So to summarize

//constructors
const vector straightUp( 0, 1, 0 );
const vector straightRight( 1, 0, 0 );
const vector straightLook( 0, 0, 1 );

vector lookAt = (targetPos - eyePos).normalized();

if( straightUp.dot( lookAt ) < sineOf(89.9) ) //pitch is less than 89.9 degrees
{
vector myRight = lookAt.cross( straightUp ).normalized();

vector trueUp = myRight.cross( lookAt ); //we don't need to normalize, both vectors are normal and are perpendicular, so sin(theta) is 1

return Matrix( eyePos, lookAt, myRight, trueUp );

}
else{
//save for later
}

##### Share on other sites

Hello,

I've wrote 3D math library for C, it is easy to use and used SIMD/AVX if possible,

all funcs are inlined but also it is possible to use pre compiled version,

e.g. glm_mat4_mul is inline and glmc_mat4_mul is compiled, c means call:

https://github.com/recp/cglm

Here how to I implement rotate vector around axis:

Rotate vector around axis:

https://github.com/recp/cglm/blob/master/include/cglm-vec.h#L397

Rotate vector using affine transform:

https://github.com/recp/cglm/blob/master/include/cglm-vec.h#L467

Edited by recp

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628701
• Total Posts
2984287

• 22
• 10
• 9
• 13
• 13