Fast way to get spherical coordinates

Started by
3 comments, last by Viik 12 years, 11 months ago
I have a 2d texture that I'm reading using sperical coordinates. So two angles theta and phi corresond to uv of texture. As input I'm using xyz vector, that is converted into theta and phi.

Right now my code in hlsl looks like this:


static const float pi = 3.14159265f;
float2 toSphere(float3 n)
{
float phi = 0;

if((n.x>=0) && (n.z>=0))
{
phi = atan(n.z/n.x);
}
if((n.x<0) && (n.z>=0))
{
phi = atan(n.z/n.x);
phi += pi;
}
if((n.x<0) && (n.z<0))
{ phi = atan(n.z/n.x);
phi += pi;
}
if((n.x>=0) && (n.z<0))
{
phi = atan(n.z/n.x);
phi += 2 * pi;
}
phi /= 2.f * pi;

float theta = acos(n.y);
theta /= pi;

return float2(phi, theta);
}



It works correctly and basically just an direct implementation from general math formulas of relation between cartezian and spherical coordinates.
But I don't think that this really an efficient implementation, there are almost no math but four branches and that's not good for shader in the way how it's used (it should break cashing). I hope somebody can direct me towards more effeicient implimentation, I know in past spherical mapping was used a lot. Hope somebody remembers "old tricks" that he could share.

Advertisement
I am sure there is a more efficient way to get the angles. I would say, you schould never ever use an if statement on the gpu.

But what is the idea behind storing UV coordinates as a Vector? If you would use 2 floats per vertex it would cost less memory and as you can see, the reconstruction is not simple at all. I don't know how exact your reconstruction is but there is a possibility that float inaccuracy causes problems.
E=mc^2 + 2d6
Optimizing by using atan2:

AFAIK HLSL supports the atan2 function, and hence the entire case distinction can be conceded to a predefined function.

Otherwise, optimizing the if-clauses:

In every of the 4 if-clauses the same statement
[color="#1C2837"][color="#000000"]phi [color="#666600"]=[color="#000000"] atan[color="#666600"]([color="#000000"]n[color="#666600"].[color="#000000"]z[color="#666600"]/[color="#000000"]n[color="#666600"].[color="#000000"]x[color="#666600"]);
occurs using the same arguments in any case. So move that statement in front of the if-clauses. Then notice that the 1st if-clause's body is now empty and hence the clause can be dropped. The body of the 2nd and 3rd if-causes are identical and hence can be combined in a single if-clause (but see the comment below).

However:

Your implementation has some flaws. It suffers from division-by-zero problems. The method used to compute the case where both arguments are less than 0 is AFAIK wrong (you add pi instead of subtracting it).
As heagarr said, HLSL supports atan2.
This is what I'm using. Not particularly optimized but it should be faster than yours. If you want 'y' to be 'theta'-axis you have to change the swizzles.

float3 CartesianToSpherical( in float3 xyz )
{
float r = length( xyz );
xyz *= 1.f/r;
float theta = acos( xyz.z );
float phi = atan2( xyz.y, xyz.x );
phi += ( phi < 0 ) ? 2*PI : 0; // only if you want [0,2pi)

return float3( phi, theta, r );
}
Thanks guys! Exactly what I need.

This topic is closed to new replies.

Advertisement