Sign in to follow this  
tenpoundbear

Spherial coordinates to cartesian coordinates help

Recommended Posts

tenpoundbear    167
Hey guys, I am trying to convert spherical coordinates to cartesian coordinates in my program but I having trouble understanding the conversion. I understand the above image is showing how points are worked out on a sphere in spherical coordinates. I am more comfortable with degrees notation then radians, so I will tend to use degrees. In my program the angle from the positive Z-axis I've defined as the phi symbol and ranges from -90 to 90 degrees (90 being the north pole, -90 being the south pole and 0 being the middle). And the angle from the positive X-axis is defined as the theta symbol ranging from 0 to 360 degrees. So now, my research has shown me that you can convert the spherical coordinates to cartesian coordinates via these formulas
x = radius * sin(phi) * cos(theta)
y = radius * sin(phi) * sin(theta)
z = radius * cos(phi)
I've have a couple of questions... First is this... the axises in the image above is different to the cartesian coordinates! I mean, usually x-axis is horizontal line, y-axis is vertical line and z-axis is into screen (directX convention). So I am a bit confused about that... I did read somewhere that this was so that the poles would all line up nicely (doesn't matter, I am more concerned about other things to be honest). But my question is this... So once the spherical coordinates are converted to cartesian coordinate, is it a matter of just using the standard cartesian coordinate system with the derived values I get? My second question is a follow on from my first. I assumed the answer to my first question is a yes, and I've attempted to test this in my code with results that seemed to be incorrect. Although, I am not sure if it is correct or incorrect. Let me demonstrate with a snippet of my code.
void Skydome::GenerateDome( float radius, float num_slices, float num_segments, bool sphere )
{
// ASSUME
// radius = 1
// num_slices = 10, therefore delta value is 90 degrees
// num_segments = 10, therefore delta theta value is 90 degrees

	// Generate the dome
	float delta_phi = 9.0f;
	float delta_theta = 36.0f;
	
	int n = 0;
	// Starting in middle of sphere (where latitude = 0 degrees)
	for( float phi = 0; phi < 90; phi += delta_phi )
	{
		for( float theta = 0; theta < 360; theta += delta_theta )
		{
						// Calculate the vertex at phi, theta
			Vertices[ n ].pos.x = radius * sinf( DegToRad( phi )) * cosf( DegToRad( theta ));
			Vertices[ n ].pos.y = radius * sinf( DegToRad( phi )) * sinf( DegToRad( theta ));
			Vertices[ n ].pos.z = radius * cosf( DegToRad( phi ));
			n++;
			
			// Calculate the vertex at phi+dphi, theta
			Vertices[ n ].pos.x = radius * sinf( DegToRad( phi + delta_phi )) * cosf( DegToRad( theta ));
			Vertices[ n ].pos.y = radius * sinf( DegToRad( phi + delta_phi )) * sinf( DegToRad( theta ));
			Vertices[ n ].pos.z = radius * cosf( DegToRad( phi + delta_phi ));
			n++;

			// Calculate the vertex at phi, theta+dtheta
			Vertices[ n ].pos.x = radius * sinf( DegToRad( phi )) * cosf( DegToRad( theta + delta_theta ));
			Vertices[ n ].pos.y = radius * sinf( DegToRad( phi )) * sinf( DegToRad( theta + delta_theta ));
			Vertices[ n ].pos.z = radius * cosf( DegToRad( phi ));
			n++;

			if (phi > -90 && phi < 90)
			{
				// Calculate the vertex at phi+dphi, theta+dtheta
				Vertices[ n ].pos.x = radius * sinf( DegToRad( phi + delta_phi )) * cosf( DegToRad( theta + delta_theta ));
				Vertices[ n ].pos.y = radius * sinf( DegToRad( phi + delta_phi )) * sinf( DegToRad( theta + delta_theta ));
				Vertices[ n ].pos.z = radius * cosf( DegToRad( phi + delta_phi ));
				n++;
			}
		}
	}
}




The output values for the first 3 vertex's position vector is
 n: 0 X position: 0.000000 Y position: 0.000000 Z position: 1.000000
 n: 1 X position: 0.156434 Y position: 0.000000 Z position: 0.987688
 n: 2 X position: 0.000000 Y position: 0.000000 Z position: 1.000000
Now looking at '0' and '2' I can see that both the same which is totally wrong (I think), and I don't understand why. I am trying to do the following. The vertex at 0 and 2 should be different, because different position on the sphere. Can anyone help? [Edited by - tenpoundbear on May 3, 2010 5:21:18 AM]

Share this post


Link to post
Share on other sites
haegarr    7372
Naming the axes x, y, and z is just a convention; they can be named as you wish. So there is no problem in using another convention like
x = radius * sin(phi) * cos(theta)
y = radius * cos(phi)
z = radius * sin(phi) * sin(theta)
or something else. Simply pick what serves your needs. Just keep in mind that the poles are generated where the sin(phi) terms are in. In your example the poles lie on the z axis, in my example they lie on the y axis.

Now, chosing phi==0° or phi==180° generates a pole due to the shown use of the sin(phi) terms! So I'd say you're wrong with your assumption that phi ranges from -90° to 90°; instead I would say it ranges from 0° to 180°. Then picking phi==90° (i.e. the equator) you will probably get what you expect.

Share this post


Link to post
Share on other sites
tenpoundbear    167
What do you mean exactly? I am trying to figure out how you you come to the conclusion that my poles lie on the z-axis and your poles lie on the y-axis. I just don't see the connection there... :(

Quote:
Original post by haegarr
keep in mind that the poles are generated where the sin(phi) terms are in. In your example the poles lie on the z axis, in my example they lie on the y axis.


For my equations

x = radius * sin(phi) * cos(theta)
y = radius * sin(phi) * sin(theta)
z = radius * cos(phi)


I have the term sin(phi) appearing in 3 parts... I don't know, I just don't understand. Could you clarify even more if possible?

If I can understand the above I may be able to follow what you mean here.
Quote:
Original post by haegarr
Now, chosing phi==0° or phi==180° generates a pole due to the shown use of the sin(phi) terms! So I'd say you're wrong with your assumption that phi ranges from -90° to 90°; instead I would say it ranges from 0° to 180°. Then picking phi==90° (i.e. the equator) you will probably get what you expect.


At the moment everything is like "WTF" is he talking about lol (no disrespect intended).

Appreciate everything you can help me with.

Share this post


Link to post
Share on other sites
mrr    150
The poles are at the position there changing theta will not change x,y,z.
And that is in your case there phi is 0 and pi, also there z is 1 and -1.

And why n=2 equal to n=0.. probably because you are starting at the pole phi=0.

Thus in step 2 to you go back to phi=0..and rotate a bit around theta but at the pole rotations around theta do not matter..same output.

Share this post


Link to post
Share on other sites
haegarr    7372
mrr has already explained most of my prevous answer. Just to make if absolutely clear:

If sin(phi) is 0 then cos(theta) or sin(theta) can be any value without having the chance to change the values of the products
radius * sin(phi) * cos(theta)
radius * sin(phi) * sin(theta)
because "0 multiplied by anything is 0". And sin(phi) is 0 if phi is an integer multiple of 180°. For those values of phi the product
radius * cos(phi)
will be either -radius or +radius. So there are 2 situations where surface segments on such a sphere "collapse" to a single point; these are called the poles.

The poles are hence at co-ordinates [ 0, 0, +/- radius ] if you order the terms as in the OP, and they will be at [ 0, +/- radius, 0 ] if they are ordered as in my answer above.

Since your outer loop starts at phi==0 you actually operate at a pole, but you expected (see the image in the OP) to operate at the equator.

Share this post


Link to post
Share on other sites
tenpoundbear    167
I see, and I do understand it now.

Still wasn't understanding it with mrr's explanation, but finally was able to with the extra explanations by haegarr.

I've have edited this post completely since no one was able to give an answer to my original question. That's no problem, I understand.

While I understand haegarr's explanation I am still unable to draw/render my own sphere geometry.

This image/extract is taken from a PDF The Chinese University of Hong Kong, Department of Mathematics.



If I wanted my cartesian coordinates to have positive y-axis pointing up, positive x-axis pointing right and positive z-axis pointing into the screen, what formula do I have to use?

I understood what you were saying about having the north poles on the y-axis, hence I have this for my y-axis

y = radius * cos(phi)

but I don't think my other axises are consistent.

I see many formulas and they all vary slightly... some may have the theta and phi values interchanged, some have the sine and cosine interchanged etc...

To be honest I am confused with all this.

What I want is just the standard cartesian system as stated above.

At the moment I have this

x = radius * sine( phi ) * cosine( theta );
y = radius * cosine( phi );
z = radius * sine( phi ) * sine( theta );


With the formulas I have above, I have my phi representing the zenith angle from the z-axis in the spherical coordinate system (refer to OP image), and the theta representing the azimuth angle from x-axis in the xy plane in the spherical coordinate system (refer to OP image).

Therefore, my for loop in my code goes like this

//I am working from north pole, make a full 360 degrees rotation then move down and repeat process.
for( float phi = 0; phi < 90; phi += delta_phi )
{
for( float theta = 0; theta < 360; theta += delta_theta )
{
}
}


I am afraid to actually ask this, but is anyone able to help me here? Is the order of my r, phi, theta consistent with the orientation of my x, y and z?

[Edited by - tenpoundbear on May 7, 2010 11:20:25 AM]

Share this post


Link to post
Share on other sites
david_watt78    133
here is how i do conversion from unit sphere to longitude and latitude

Out.UV.y = Out.Normal.y*-1;
Out.UV.x = degrees(atan2(Out.Normal.z,Out.Normal.x))/180; //longitude
Out.UV.x = saturate((Out.UV.x + 1)/2);
Out.UV.y = saturate((Out.UV.y+1)/2);

since this is shader code normal is the unit sphere coords and UV is texture coords in the range 0-1.For longitude you will need to add 180 instead of dividing by 180(if you want the range of 0-360).Do a degree(acos(Out.Normal.y)) in order to get latitude in the range of +- 90.I don't use the acos since it is unnecessary in this case as I only need texture coords for this example. To explain the simplicity of the latitude case the unit sphere y coords is the cos value so no atan2 is needed in that case.

Share this post


Link to post
Share on other sites
johnstanp    267
Quote:
Original post by tenpoundbear
Sorry... I haven't had any replies and the post has moved of the radar a bit.

Anyone able to offer some help?


To be honest, I never memorize things but how to derive a formula. You shouldn't be worried by what is called x, y, z, theta and phi. You should know how to project a vector into a another. What you call the component of vector V along the x-axis, is simply its projection into the x-axis, a dot product of V and the unit vector of the x-axis. How do we compute the dot product of two vectors when we know their respective magnitudes and the angle between them?

Whatever "the" x-axis might be, the projection of a vector V into it, will always be:

x( projection of V into the x axis. It is the component of V along the x-axis ) = dot_product( V, unit_vector( x_axis ) )

x = magnitude( V ) * magnitude( unit_vector( x_axis ) ) * cos( angle( V, unit_vector( x_axis ) )
<=> x = magnitude( V ) * 1.0 * cos( angle( V, x_axis ) )

What are your axes?
Quote:



If I wanted my cartesian coordinates to have positive y-axis pointing up, positive x-axis pointing right and positive z-axis pointing into the screen, what formula do I have to use?

The positive z-axis is pointing into the screen.
The positive y-axis is pointing up.
The positive x-axis is pointing right.

Well, the axes you chose form a left-handed coordinate system. The one represented is right handed. The "x" of the graphic will become your "-z", the "y" of the graphic, your "x" and the "z" of the graphic, your "y". So,

(x,y,z)=>(-z,x,y)

If you keep the same definitions for theta, rho and phi, you will then have the following formulas:
-z = rho * sin( phi ) * cos( theta )
x = rho * sin( phi ) * sin( theta )
y = rho * cos( phi )

This cannot be easier.

Well, if you master trigonometry, deriving those formulas without any previous knowledge is quite easy.

The projection of the vector V( whose origin is the origin of the system of coordinates and extremity is a point lying on the sphere ) in the horizontal plane, is

magnitude( v_projected_on_the_xz_plane ) = magnitude( V ) * cos( pi - phi )
<=>magnitude( v_projected_into_the_xz_plane ) = rho * sin( phi )( cos( pi - lambda ) = sin( lambda ) )

How can you find it?
Well, the vector V, its projection into the xz plane and is projection into the y-axis form a right triangle. The right angle being the angle between the projections into the xz plane and the y-axis. By simply applying the relations between the sides of a right triangle( definitions of cosinus and sinus ), we inevitably find:
magnitude( v_projected_into_the_xz_plane ) = rho * sin( phi )
and
v_projected_into_the_y_axis = rho * cos( phi )( the angle between V and the y-axis being equal to phi )

The second formula is also derived from the relations of the sides of right triangles.
Those relations are simply:
*the length of the adjacent side is equal to the length of the hypothenuse multiplied by the cosinus of the angle between these sides
*the length of the opposite side is equal to the length of the hypothenuse multiplied by the sinus of then angle between the hypothenuse and the adjacent sides.

For the definition of hypothenuse, adjacent and opposite sides, wikipedia should a wonderful article explaining them. Here:
v_projected_into_the_y_axis is the adjacent side of the right triangle, v_projected_into_the_y_axis, hypothenuse and projection_into_the_xz_plane.
v_projected_into_the_xz_plane is the opposite side of the same right triangle.

Well, where were we?
Ah, oui...
v_projected_into_the_y_axis = rho * cos( phi ) = y

What about the x_axis projection?
The projections of V into the xz plane, the x-axis and the z-axis, form a right triangle, the right angle being the angle between the x-axis and z-axis projections. Using trigonometric formulas, we have:

x = v_projected_into_the_x_axis = v_projected_into_the_xz_plane * sin( theta )
( v_projected_into_the_x_axis being the opposite side in the right triangle formed by v_projected_into_the_x_axis, v_projected_into_the_xz_plane and v_projected_into_the_z_axis )

<=> x = rho * sin( phi ) * sin( theta )

z = v_projected_into_the_z_axis = -v_projected_into_the_xz_plane * cos( theta )
( v_projected_into_the_z_axis is the adjacent side )

<=> z = -rho * sin( phi ) * cos( theta )
Why the sign "-"?
Because, if theta = 0 and phi = pi / 2, then z < 0. It can also be justified by the fact that in that configuration the angle between the z-axis and the vector V is greater than pi/2( the two vector are facing "opposite" directions )

[Edited by - johnstanp on May 7, 2010 11:48:43 PM]

Share this post


Link to post
Share on other sites
tenpoundbear    167
Lots of info there... finding it very hard to comprehend all of it.

Math is not my strong point (but am taking steps to improve it). At the moment I think the complex mathematical understanding behind all these formulas are beyond me.

I am basing my code on this tutorial at the moment, Spherical Coordinates in C#, since I am pretty sure it is using the left handed system.

I wanted to ask would anyone know why this single triangle will not show/render?

The 3 vertices are

(0, 1, 0)
(0, 0, -1)
(-1, 0, 0)


I've made sure they are in a clockwise order.

RENDER SECTION OF CODE

// ... Other code omitted
D3DDevice->SetFVF( VERTEX_FVF_Sky );
D3DDevice->SetStreamSource( 0, VBSkydome, 0, sizeof( skydome->getSkydomeVertices() ));
if( FAILED( D3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 3, 1 )))
{
MessageBox( 0, "FAILED", 0, 0 );
}

// THIS COMMENTED OUT CODE WILL SHOW THE FIRST TRIANGLE BTW
//if( FAILED( D3DDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 )))
//{
// MessageBox( 0, "FAILED", 0, 0 );
//}
// ....



I'm rendering 1 triangle, and the the 2nd triangle in the vertex buffer only, hence I start at vertex #3 as seen above.

In case anyone wants to know where I generated the geometry for the vertices...

void Skydome::GenerateDome( float radius, float sliceNum, float segmentNum, bool sphere )
{
// Some code omitted...

float sliceSize = sphere ? ( 180.0f / sliceNum ) : ( 90.0f / sliceNum );
float segmentSize = ( 360.0f / segmentNum );

// Assume 2 slices, 4 segments only
float deltaPhi = sliceSize; // For testing purposes this = 90 degrees
float deltaTheta = segmentSize; // For testing purposes this = 90 degrees

int n = 0;
for( float phi = 0; phi <= 180; phi += deltaPhi )
{
for( float theta = 0; theta <= 360; theta += deltaTheta )
{
// Only generating vertices for the top half of sphere, 4 faces if there are 4 segments
if( phi == 0 )
{
// Calculate the vertex at north pole
Vertices[ n ].pos.x = radius * sinf( DegToRad( phi )) * cosf( DegToRad( theta ));
Vertices[ n ].pos.y = radius * cosf( DegToRad( phi ));
Vertices[ n ].pos.z = (-1 * radius) * sinf( DegToRad( phi )) * sinf( DegToRad( theta ));
n++;

Vertices[ n ].pos.x = radius * sinf( DegToRad( phi + deltaPhi )) * cosf( DegToRad( theta ));
Vertices[ n ].pos.y = radius * cosf( DegToRad( phi + deltaPhi ));
Vertices[ n ].pos.z = (-1 * radius) * sinf( DegToRad( phi + deltaPhi )) * sinf( DegToRad( theta ));
n++;

Vertices[ n ].pos.x = radius * sinf( DegToRad( phi + deltaPhi )) * cosf( DegToRad( theta + deltaTheta ));
Vertices[ n ].pos.y = radius * cosf( DegToRad( phi + deltaPhi ));
Vertices[ n ].pos.z = (-1 * radius) * sinf( DegToRad( phi + deltaPhi )) * sinf( DegToRad( theta + deltaTheta ));
n++;
} //END IF
} //END FOR
} //END FOR
} // END



Very grateful for the help so far :)
But do you know why I only get a black screen... no triangles rendered?

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