Sign in to follow this  
Heelp

how to find x,y,z of a point in a sphere if I'm in the center of a sphere?

Recommended Posts

Guys, If you have played CS 1.6, I want to make my player to look around when I move the mouse up/down/left/right like in a fps game. I found the code, I spent all day trying to understand it and I still can't and I was wondering if someone can explain it to me.

 

  Imagine that my camera is always at point ( 0, 0, 0 ). I need to set the point where the camera looks at. And I need that point to be on ( to lie on ) a sphere with radius = 1.0f. And when I move the mouse, I need the lookat point to be changed, but to lie on the sphere again. So basically I move my lookat point around a sphere with the mouse, the bigger the sphere is, the less sensitivity the mouse has.( and my coordinates are on the center on the sphere, of course). First I tried to do just left/right mouse movement, without up/down, and this was easy, just set the lookat point with                    ( cos(angle), y, sin(angle) ). But when I need to progress from circle to sphere, sh*t gets real, because when I move up/down, I mess up my left/right coordinates.

The code I found is this:

glm::vec3 direction(
cos(verticalAngle) * sin(horizontalAngle),
sin(verticalAngle),
cos(verticalAngle) * cos(horizontalAngle)
);

I just need to sum this direction vector with my currentPosition vector and to use it as my lookAt point and the joke is that it actually works. It is done like this:

 

ViewMatrix = glm::lookAt(
position, // Camera is here
position+direction, // and looks here : at the same position, plus "direction"
up // Head is up (set to 0,-1,0 to look upside-down)

);

 

 

   If you got anything from what I said, I will be really grateful if you somehow manage to tell me why this really works, because this logic is just beyond me. :wacko:


EDIT: Ok, I've made some progress in trying to understand this crazy logic. Basically, when I am looking straight "forward" , "y" is 0. And as I move the mouse UP and look to the skies, the y gets bigger, for example: y = 0 goes to y = 0.677465. And as the "y" gets bigger, the x and z get smaller by the same ratio, that's why "x" and "z" coordinates depend on the "y" coordinate. And that's why there is cos(verticalAngle) in both x and y. But now I don't understand why the cos(verAngle) is multiplied by the sin(horAngle) ( I'm talking about "x" here )???

Edited by Heelp

Share this post


Link to post
Share on other sites

I will assume 'up' is y. First you do yaw as you have done, so you rotate about the y-axis. Next you want to do pitch but instead of rotating about the x-axis as you think you might you first need to rotate the x-axis about the y-axis and then rotate about the new axis if that make sense.

 

Do you have access to quaternions? I will use those but it's basically the same method when using matrices (just more expensive).

// How you decide to turn mouse movement into an angle is up to you, you can do your sensitivity stuff here
// but for simplicity sake I will assume it's already done
float yawAngle = ...
float pitchAngle = ...

// Do yaw first though order isn't too important
Quaternion yaw = Quaterion::FromAxisAngle(Vector::UP, yawAngle);
// Now you can't just rotate about 'Right' to do pitch, you need a new Vector to rotate about
Vector newAxis = yaw*Vector::RIGHT;
Quaternion pitch = Quaternion::FromAxisAngle(newAxis, pitchAngle);
Quaternion final = pitch*yaw;
Vector direction = final*Vector::FORWARD;

That should work. The important part to note is finding the second axis to rotate about. glm should have some similar functions for creating axis-angle matrices if you need those, it should work the same way (it's just a lot more work for the computer that way).

 

you could possibly do pitch first too which does seem neater:

float yawAngle = ...
float pitchAngle = ...

Quaternion pitch = Quaterion::FromAxisAngle(Vector::RIGHT, pitchAngle);
Quaternion yaw = Quaternion::FromAxisAngle(Vector::UP, yawAngle );
Quaternion final = yaw*pitch;
Vector direction = final*Vector::FORWARD;
Edited by Nanoha

Share this post


Link to post
Share on other sites

I didn't formulate my question well, that's why you misunderstood me. The code I posted is actually working, but I don't quite understand all the logic behind it, but it is working, but I don't know why. And your code is working too, I'm sure, but why and how? :wacko: ( but thanks for the answer, anyways ).

Edited by Heelp

Share this post


Link to post
Share on other sites

In fact, the calculation of glm::vec3 direction is just an incarnation of standard conversion from so-called spherical co-ordinates into cartesian co-ordinates. The spherical co-ordinates are given by the 2 angles and the radius being 1, so that you have a point on the surface of the unit sphere. The "direction" is then the vector from the center of the sphere to the point on the surface, but now expressed as (x,y,z) length triple.

 

The actual magic, so to say, happens in fact when calculating the both angles from the mouse position; but that stuff isn't shown in the OP.

Share this post


Link to post
Share on other sites

If you construct 2D vector (sin(angle), cos(angle)), the result is always a unit vector (length of 1) no matter what the angle is.

We just need 2 perpendicular directions (90 degrees between them), and add them multiplied by sin and cos:

 

vec2 (sin(angle), cos(angle))

=

vec2(1,0) * sin(angle)

+

vec2(0,1) * cos(angle)

 

 

 

 

 

This property is used in the first snippet to first generate a rotated unit vector in the xz plane:

 

glm::vec3 direction(
sin(horizontalAngle),
0,
cos(horizontalAngle)
);

 

This is again a unitvector pointing at some direction with unit length.

Now you want to rotate this around the vertical axis - we don't know yet what will happen with x&z,

but y is zero and have to change - it must become either sin(verticalAngle) or cos(verticalAngle):

 

glm::vec3 direction(
sin(horizontalAngle),
sin(verticalAngle),
cos(horizontalAngle)
);

 

Animating verticalAngle would just show y moving between -1 and +1.

That's no rotation because we still miss the cos(verticalAngle) part.

What should we do with it?

 

Now twist your brain and imagine we already have a unit vector from horizontalAngle in xz plane.

We also have another vector (0,1,0) already multiplied with sin(verticalAngle).

And also we see those two vectors are guaranteed to be perpendicular (90 degrees between).

 

Heureka - Going to multiply the xz direction with cos(verticalAngle), like we did with the initial 2D example:

 

 

glm::vec3 direction(
sin(horizontalAngle)     * cos(verticalAngle),
sin(verticalAngle),
cos(horizontalAngle)    * cos(verticalAngle)
);

 

That's it - we can point in any direction in 3D space and the result is always a unit vector.

 

Hope this helps - it's a very simple thing but hard to explain.

 

The second code snippet is a bit harder... let's see if you've got the first one before :D

 

 

EDIT: I think i can write a much better explantation, but no time at the moment...

Edited by JoeJ

Share this post


Link to post
Share on other sites
haegarr:In fact, the calculation of glm::vec3 direction is just an incarnation of standard conversion from so-called spherical co-ordinates into cartesian co-ordinates. The spherical co-ordinates are given by the 2 angles and the radius being 1, so that you have a point on the surface of the unit sphere. The "direction" is then the vector from the center of the sphere to the point on the surface, but now expressed as (x,y,z) length triple.

 

Very true. What I'm not getting is how the angles multiplication does its magic.

 

 

 

 

JoeJ:

glm::vec3 direction(

sin(horizontalAngle),
0,
cos(horizontalAngle)

);

 

Ok, I think I got the first snippet. If "up" parameter is set to positive "y" and horizontalAngle is 10 degrees, then when the horizontalAngle is incremented, the X  increments too, and the Z gets decremented.( in the situation when 0<horizontalAngle<90 ). And in openGL we use rightHand rule, this means that the X positive side is left and Z positive is forward . And the more bigger X gets, the more I go to the left ( again talking in 0<horAngle<90 ). This is the circle. But in order to connect the verticalAngle to the equation, we need to put it somewhere in "y". And here is your second part of the code:

glm::vec3 direction(
sin(horizontalAngle),
sin(verticalAngle),
cos(horizontalAngle)
);

And now it's going up and down, it's rotating around some circle. Correct me if I'm wrong, but I suspect that the figure we look at, when changing the verticalAngle in this code is not a circle, but a line, because the X and Z remain the same, just Y gets bigger and smaller. Is that true? ( Yes, and I missed that "heureka" moment )

Edited by Heelp

Share this post


Link to post
Share on other sites

I didn't formulate my question well, that's why you misunderstood me. The code I posted is actually working, but I don't quite understand all the logic behind it, but it is working, but I don't know why. And your code is working too, I'm sure, but why and how? :wacko: ( but thanks for the answer, anyways ).


There's a few things to consider with this, first there's the theory of it, then there's the maths of it and finally the implementation of it. I like to work things like this out by sticking my arm out in front of me (I call this forward(0,0, 1) but in OpenGL it would be (0, 0, -1)) and that is my look direction by default. I pretend I am in a huge sphere where my fingertips will always touch the inner surface. I think the second method I posted is easier so I'll do that. First it's doing pitch so I just rotate my arm up or down by verticalAngle, then it does yaw so I'd rotate on my chair horizontalAngle and my arm should then be pointing in the desired direction. That's the theory.

Mathematically I am just doing x-axis rotation of verticalAngle followed by y-axis rotation of horizontalAngle.

You just implement that in whatever way you choose. The code you have is doing the same thing - it is using a direction of (0, 0, 1) as forward. I don't know how they derived this but it goes like this:
x-axis rotation matrix (pitching) where V is vertical angle

1 	0 	0
0 	cos(V) 	-sin(V) 
0 	sin(V) 	cos(V) 

y-axis rotation matrix (yawing) where H is horizontal angle

cos(H)	0	sin(H)
0	1	0
-sin(H)	0	cos(H) 

You multiply those matrices to get a single matrix that does pitch followed my yaw and you get:

cos(H)	sin(V)*sin(H)	cos(V)*sin(H)
0	cos(V)		-sin(V) 
-sin(H)	sin(V)*cos(H)	cos(V)*cos(H) 

Then your original forward is (0, 0, 1) so as a matrix that is

0
0
1 

Multiply that by the final matrix and you get:

cos(V)*sin(H) 
sin(V) 
cos(V)*cos(H) 

You may recognise those terms from the code you have:

glm::vec3 direction(
cos(verticalAngle) * sin(horizontalAngle),
sin(verticalAngle),
cos(verticalAngle) * cos(horizontalAngle)
);

Whether they got it the same way as I did I can't say but it does show that this is the exact same way just done far quicker than multiplying matrices (which is good).

Edited by Nanoha

Share this post


Link to post
Share on other sites


Very true. What I'm not getting is how the angles multiplication does its magic.

Well, there is no angle multiplication. The sine and cosine functions compute the lengths of a unit-vector as projection onto cartesian axes, where the vector is rotated by the given angle. The multiplication is done because we have 3 dimensions. Again, this is what makes spherical co-ordinates. For a circle we need an angle (and a radius), and for a sphere we need 2 angles (and a radius).

Share this post


Link to post
Share on other sites

Ok, I got to that pitch*yaw matrix ( or yaw*pitch, I got your result with yaw*pitch . And I multiplied by "forward" and it really works. But now another question comes, maybe I'm just tired, but why multiplying this by my original forward or backwards or +x or -x works, but if I multiply the matrix by my original "y", it doesn't work?

Share this post


Link to post
Share on other sites

Ok, I got to that pitch*yaw matrix ( or yaw*pitch, I got your result with yaw*pitch . And I multiplied by "forward" and it really works. But now another question comes, maybe I'm just tired, but why multiplying this by my original forward or backwards or +x or -x works, but if I multiply the matrix by my original "y", it doesn't work?

 

What do you mean 'doesn't work'? It will work with forward because it is designed to work that way but if you start throwing random vectors in it's no longer guaranteed to work because what you ask of it doesn't necessarily make sense so it depends what you are asking it to do. I assume you are not but you can't just put in 'up' as your original vector and then stick that in as your camera direction, depending on the angle it might not make sense. You sometimes lose a degree of freedom with rotations too (depending on how you rotate) so something along those lines might be coming in to play too.

 

Think about what happens in you look up 90 degrees, now if you yaw you are still looking up. You then put that in your camera (0, 1, 0) and also you put 'up' as being (0, 1, 0) which doesn't make sense in the lookat matrix (I don't think you can use two parallel vectors). The way to solve that is to have both a forward vector and an up vector that gets rotated rather than just the forward vector.

 

I can't really say for sure without know what effect it has.

Share this post


Link to post
Share on other sites

True story. Thanks very very very much for the explanation, really. I'm mad at myself now, because I messed up the topic's name and I can't rename it properly, so other people can come here and see how this works, because I searched everywhere, but there weren't any explanations that were nearly as good as this one. TY MAN  :cool:  

Edited by Heelp

Share this post


Link to post
Share on other sites


EDIT: I think i can write a much better explantation, but no time at the moment...

 

... i'll add the 'better' explantation anyways, because it requires no matrix knowledge.

 

It's about using two perpendicular unit vectors and sin/cos again, i'll refer to the vectors as basis vectors, e.g.:

vec3 basis0 (1,0,0);
vec3 basis1 (0,0,1);

We can draw a circle to visualize how to calculate a angled direction from them:

for (float angleH = 0; angleH < PI*2; angleH+=PI/100)
{
vec3 dir =
basis0 * sin(angleH)
+
basis1 * cos(angleH);
 
RenderPoint (dir); // dir always unit length -> nice circle
}

 

Understanding this is all you need, because the second angle we handle next uses the same math:

basis0 =
vec3 (1,0,0) * sin(angleV)
+
vec3 (0,1,0) * cos(angleV);

basis0 has been rotated in the xy plane, but it is still unit length and perpendicular to basis1.

 

You can repeat the circle rendering code here and it will show a rotated circle.

 

Then you can optimize the code and you will get the same kind of math as in your first code snippet.

This should help to understand it well.

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