Spherical projection on a cube

September 10, 2011

I decided to apply an earth texture to my sphere/cube. Standard technique is to use TEXCOORD, but things get a bit messy when you are working with a cube as your base. Particularly at the top and bottom faces. Traditionally you must have a vertex at the north and south pole to make the spherical projection work properly.  Since I want to leverage the direct3d11 tessellation features, I needed to find a different solution.  My first attempt was to calculate the TEXCOORD in the domain shader.

float phi = acos(norm.y);
float v = phi / PI ;
float u = acos(max(min(norm.x / sin(phi), 1.0), -1.0)) / (2.0 * PI);
if (norm.z >0 )
	u = 1 - u;
Output.vTexCoord.x = u;
Output.vTexCoord.y = v;

However I found a nasty seam issue when the TEXCOORD.x wrapped from 1 to 0. In directx9 there is a WRAP0 function in the renderstate that supposedly resolves this issue.
But in directx10 and higher there is no such WRAP0 function, apparently it has been depreciated with no replacement. The articles I read suggested Microsoft recommends you leverage your underlying geometry to fix the 1 and 0 coordinates on a vertex. That is the solution I normally used with directx9 when I constructed my spheres by hand, doubling up on the vertices at the seam.

My second attempt was to move the spherical projection code into the hull shader. This worked great until I noticed the seam again. It is smaller (only a pixel wide) but extends across the z=0 plane. I struggled with this for several days, but eventually stumbled upon a solution. The trick is to do the UV calculations within the sampler call. I was making a separate u variable and then when normal.z < 0 was setting it to 1-u to account for the back side of the sphere/planet. Moving the 1-u into the sampler call magically fixed the seam. I suspect is has something to do with the precision of the float values, but I’m not really sure.

float3 norm = normalize(input.vPlanetNormal);
	float phi = acos(norm.y);
	float v = phi / PI ;
	float u = -acos(max(min(norm.x / sin(phi), 1.0), -1.0)) / (2.0 * PI);
	float4 diffuse;
	float4 diffuse1 = g_texture.Sample(g_samLinear, float2(u,v));
	float4 diffuse2 =  g_texture.Sample(g_samLinear, float2(1-u,v));
	if (norm.z &lt; 0)
		diffuse = diffuse2;
		diffuse = diffuse1;

Leave a Reply

You must be logged in to post a comment.