Parametric Equation of a Sphere and Texture Mapping

Written by Paul Bourke
August 1996


One possible parameterisation of the sphere will be discussed along with the transformation required to texture map a sphere. An angle parameterisation of the sphere is

where r is the radius, theta the angle from the z axis (0 <= theta <= pi), and phi the angle from the x axis (0 <= phi <= 2pi). Textures are conventionally specified as rectangular images which are most easily parameterised by two cartesian type coordinates (u,v) say, where 0 <= u,v <= 1. The equation above for the sphere can be rewritten in terms of u and v as

Solving for the u and v from the above gives

So, given a point (x,y,z) on the surface of the sphere the above gives the point (u,v) each component of which can be appropriately scaled to index into a texture image.

Note

#define PI 3.141592654
#define TWOPI 6.283185308

void SphereMap(x,y,z,radius,u,v)
double x,y,z,r,*u,*v;
{
   *v = acos(z/radius) / PI;
   if (y >= 0)
      *u = acos(x/(radius * sin(PI*(*v)))) / TWOPI;
   else
      *u = (PI + acos(x/(radius * sin(PI*(*v))))) / TWOPI;
}

There are still two special points, the exact north and south poles of the sphere, each of these two points needs to be "spread" out along the whole edge v=0 and v=1. In the formula above this is where sin(v pi) = 0.




OpenGL sphere with texture coordinates

Written by Paul Bourke
January 1999

A more efficient contribution by Federico Dosil: sphere.c


While straightforward many people seem to have trouble creating a sphere with texture coordinates. Here's the way I do it (written for clarity rather than efficiency).

Note

/*
   Create a sphere centered at c, with radius r, and precision n
   Draw a point for zero radius spheres
*/
void CreateSphere(XYZ c,double r,int n)
{
   int i,j;
   double theta1,theta2,theta3;
   XYZ e,p;

   if (r < 0)
      r = -r;
   if (n < 0)
      n = -n;
   if (n < 4 || r <= 0) {
      glBegin(GL_POINTS);
      glVertex3f(c.x,c.y,c.z);
      glEnd();
      return;
   }

   for (j=0;j<n/2;j++) {
      theta1 = j * TWOPI / n - PID2;
      theta2 = (j + 1) * TWOPI / n - PID2;

      glBegin(GL_QUAD_STRIP);
      for (i=0;i<=n;i++) {
         theta3 = i * TWOPI / n;

         e.x = cos(theta2) * cos(theta3);
         e.y = sin(theta2);
         e.z = cos(theta2) * sin(theta3);
         p.x = c.x + r * e.x;
         p.y = c.y + r * e.y;
         p.z = c.z + r * e.z;

         glNormal3f(e.x,e.y,e.z);
         glTexCoord2f(i/(double)n,2*(j+1)/(double)n);
         glVertex3f(p.x,p.y,p.z);

         e.x = cos(theta1) * cos(theta3);
         e.y = sin(theta1);
         e.z = cos(theta1) * sin(theta3);
         p.x = c.x + r * e.x;
         p.y = c.y + r * e.y;
         p.z = c.z + r * e.z;

         glNormal3f(e.x,e.y,e.z);
         glTexCoord2f(i/(double)n,2*j/(double)n);
         glVertex3f(p.x,p.y,p.z);
      }
      glEnd();
   }
}

It is a ssmall modification to enable one to create subsets of a sphere....3 dimensional wedges. As an example see the following code.

/*
   Create a sphere centered at c, with radius r, and precision n
   Draw a point for zero radius spheres
   Use CCW facet ordering
   "method" is 0 for quads, 1 for triangles
      (quads look nicer in wireframe mode)
   Partial spheres can be created using theta1->theta2, phi1->phi2
   in radians 0 < theta < 2pi, -pi/2 < phi < pi/2
*/
void CreateSphere(XYZ c,double r,int n,int method,
   double theta1,double theta2,double phi1,double phi2)
{
   int i,j;
   double t1,t2,t3;
   XYZ e,p;

   /* Handle special cases */
   if (r < 0)
      r = -r;
   if (n < 0)
      n = -n;
   if (n < 4 || r <= 0) {
      glBegin(GL_POINTS);
      glVertex3f(c.x,c.y,c.z);
      glEnd();
      return;
   }

   for (j=0;j<n/2;j++) {
      t1 = phi1 + j * (phi2 - phi1) / (n/2);
      t2 = phi1 + (j + 1) * (phi2 - phi1) / (n/2);

      if (method == 0)
         glBegin(GL_QUAD_STRIP);
      else
         glBegin(GL_TRIANGLE_STRIP);

      for (i=0;i<=n;i++) {
         t3 = theta1 + i * (theta2 - theta1) / n;

         e.x = cos(t1) * cos(t3);
         e.y = sin(t1);
         e.z = cos(t1) * sin(t3);
         p.x = c.x + r * e.x;
         p.y = c.y + r * e.y;
         p.z = c.z + r * e.z;
         glNormal3f(e.x,e.y,e.z);
         glTexCoord2f(i/(double)n,2*j/(double)n);
         glVertex3f(p.x,p.y,p.z);

         e.x = cos(t2) * cos(t3);
         e.y = sin(t2);
         e.z = cos(t2) * sin(t3);
         p.x = c.x + r * e.x;
         p.y = c.y + r * e.y;
         p.z = c.z + r * e.z;
         glNormal3f(e.x,e.y,e.z);
         glTexCoord2f(i/(double)n,2*(j+1)/(double)n);
         glVertex3f(p.x,p.y,p.z);

      }
      glEnd();
   }
}




Texture map correction for spherical mapping

Written by Paul Bourke
January 2001

Lua/gluas script contributed by Philip Staiger.


When texture mapping a sphere with a rectangular texture image with polar texture coordinates, the parts of the image near the poles get distorted. Given the different topology between a sphere and plane, there will always be some nonlinear distortion or cut involved. This normally manifests itself in pinching at the poles where rows of pixels are being compressed tighter and tighter together the closer one gets to the pole. At the poles is the extreme case where the whole top and bottom row of pixels in the texture map is compressed down to one point. The spherical images below on the left are examples of this pinching using the rectangular texture also on the left.

It is simple to correct for this by distorting the texture map. Assume the texture map is mapped vertically onto lines of latitude (theta) and mapped horizontally onto lines of longitude (phi). There is no need to modify theta but phi is scaled as we approach the two poles by cos(theta). The diagram below illustrates the conventions used here.

The pseudo-code for this distortion might be something like the following, note that the details of how to create and read the images are left up to your personal preferences.

   double theta,phi,phi2;
   int i,i2,j;
   BITMAP *imagein,*imageout;

   Form the input and output image arrays
   Read an input image from a file

   for (j=0;j<image.height;j++) {
      theta = PI * (j - (image.height-1)/2.0) / (double)(image.height-1);
      for (i=0;i<image.width;i++) {
         phi  = TWOPI * (i - image.width/2.0) / (double)image.width;
         phi2 = phi * cos(theta);
         i2  = phi2 * image.width / TWOPI + image.width/2;
         if (i2 < 0 || i2 > image.width-1) {
            newpixel = red;                         /* Should not happen */
         } else {
            newpixel = imagein[j*image.width+i2];
         }
         imageout[j*image.width+i] = image.newpixel;
      }
   }

   Do something with the output image

Applying this transformation to a regular grid is show below.

Perhaps a more illustrative example is given below for a "moon" texture. Note that in general if the texture tiles vertically and horizontally then after this distortion it will no longer tile horizontally. A number of tiling methods can be used to correct for this. One is to replicate and mirror the texture horizontally, since the distortion is symmetric about the horizontal center line of the image, the result will tile horizontally. Another method is to overlap two copies of the texture after the distortion with appropriate masks that fade the appropriate texture out at the nontiling borders.

This final example illustrates how after the distortion the image detail is evenly spread over the spherical object instead of acting like lines of longitude that get closer near the poles.




Texture Mapping Schemes in Common Usage

Written by Paul Bourke
March 1987


The following lists some of the most common texture mapping methods use by raytracing/rendering engines. The methods are illustrated by mapping the simple rectangular tile texture on the right onto a cube, sphere, and cylinder. Repeated tiling is used in all cases and where possible the tiling scale factors are kept constant.

Planar

Cubic

Cylindrical

Rectangular Cylindrical

Spherical


Miscellaneous examples

Logo for the
The Australasian Society for Psychophysiology, Inc.
VRML version of the cover for Neuroimage 8. For a paper entitled "Steady State Visually Evoked Potential Correlates of Auditory Hallucinations in Schizophrenia".