Sign in to follow this  

Spherical Texture Coordinates

This topic is 2345 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I found this article and tried using it. My texture appears upside down on the sphere. It also draws the texture twice instead of once. Once on the front of the sphere and once on the back. I understand why it is doing that after looking at thier math, but what I want is to replicate the texture coordinates as they would appear in 3ds Max. I want the texture to start from the left or -X longitude and wrap around the front to the back and end back at the -X longitude. Anyone have ideas on how to do that? Here is the sphere generation code I came up with:
//------------------------------------------------------------------------------
void GenerateSphere(ID3D10Device & device, std::vector<Buffer::SharedPtr> & buffers, float radius, unsigned divisions)
{
   // Snip long exception string

   std::vector<Position>   positions;
   std::vector<TexCoord2D> texCoords;
   std::vector<Normal>     normals;

   // Start with an empty vector of data buffers
   buffers.clear();

   // Check min divisions = 3
   if( divisions < 3 )
   {
      std::stringstream msg("Sphere requires a minimum of 3 divisions vertically and horizontally to be classified as a sphere at all.");
      msg << " divisions param: " << divisions;
      exception.m_msg = msg.str();
      throw exception;
   }

   // Check radius > 0
   if( radius <= 0.0f )
   {
      std::stringstream msg("Sphere requires a radius greater than zero.");
      msg << " radius param: " << radius;
      exception.m_msg = msg.str();
      throw exception;
   }

   // Number of points equals number of points on each circle
   unsigned numPoints = (divisions - 2) * divisions + 2; 

   // Generate positive Y pole
   Normal normal(0.0f, 1.0f, 0.0f);

   TexCoord2D texCoord(normal.x / 2.0f + 0.5f,
                       normal.y / 2.0f + 0.5f);

   positions.push_back(normal * radius);
   texCoords.push_back(texCoord);
   normals.push_back(normal);

   // Generate all of the points between poles
   float    sliceAngleXY = static_cast<float>(D3DX_PI / static_cast<double>(divisions));
   float    sliceAngleXZ = static_cast<float>(2.0 * D3DX_PI / static_cast<double>(divisions));
   unsigned midRing      = divisions >> 1;
   unsigned curLowerRing = midRing;

   for(unsigned v = 1; v < divisions - 1; ++v)
   {
      // Equator
      if( divisions % 2 == 1 && v == midRing )
      {
         for(unsigned u = 0; u < divisions; ++u)
         {
            normal.x = cos(static_cast<float>(u) * sliceAngleXZ);
            normal.y = 0.0f;
            normal.z = sin(static_cast<float>(u) * sliceAngleXZ);

            texCoord.x = normal.x / 2.0f + 0.5f;
            texCoord.y = normal.y / 2.0f + 0.5f;

            positions.push_back(normal * radius);
            texCoords.push_back(texCoord);
            normals.push_back(normal);
         }
      }

      // Upper Hemisphere
      else if( v < midRing )
      {
         float y          = cos(static_cast<float>(v) * sliceAngleXY);
         float ringRadius = sin(static_cast<float>(v) * sliceAngleXY);

         for(unsigned u = 0; u < divisions; ++u)
         {
            normal.x = ringRadius * cos(static_cast<float>(u) * sliceAngleXZ);
            normal.y = y;
            normal.z = ringRadius * sin(static_cast<float>(u) * sliceAngleXZ);
            
            texCoord.x = normal.x / 2.0f + 0.5f;
            texCoord.y = normal.y / 2.0f + 0.5f;

            positions.push_back(normal * radius);
            texCoords.push_back(texCoord);
            normals.push_back(normal);
         }
      }

      // Lower Hemisphere
      else
      {
         --curLowerRing;
         for(unsigned u = 0; u < divisions; ++u)
         {
            normal = normals[(curLowerRing - 1) * divisions + 1 + u];
            normal.y *= -1.0f;

            texCoord.x = normal.x / 2.0f + 0.5f;
            texCoord.y = normal.y / 2.0f + 0.5f;

            positions.push_back(normal * radius);
            texCoords.push_back(texCoord);
            normals.push_back(normal);
         }
      }
   }

   // Generate negative Y pole
   normal = Normal(0.0f, -1.0f, 0.0f);

   texCoord.x = normal.x / 2.0f + 0.5f;
   texCoord.y = normal.y / 2.0f + 0.5f;
   
   positions.push_back(normal * radius);
   texCoords.push_back(texCoord);
   normals.push_back(normal);

   // At this point we have:
   //
   // The positive Y pole at the first vertex
   // The negative Y pole at the last vertex
   // Rings on the XZ plane, starting at vertex 1
   // Each ring has a number of points equal to the number of divisions
   // Each ring starts at the right and circles counter clockwise
   // A number of those rings, from pos Y to neg Y, equal to the number of divisions


   // Generate indices
   std::vector<unsigned> indices;

   // Positive Y cap
   for(unsigned i = 1; i < divisions + 1; ++i)
   {
      indices.push_back(0);

      if( i == divisions )
      {
         // Wrap to first vertex in ring
         indices.push_back(1);
         indices.push_back(i);
      }
      else
      {
         indices.push_back(i+1);
         indices.push_back(i);
      }
   }

   // Inner Rings
   unsigned quadCount = 1;

   for(unsigned ring = 0; ring < divisions - 3; ++ring)
   {
      for(unsigned i = 0; i < divisions; ++i)
      {
         // 1st Triangle
         indices.push_back(quadCount);

         if(i == divisions - 1)
         {
            // Wrap to the first edge between the rings
            indices.push_back(quadCount - divisions + 1);
         }
         else
         {
            indices.push_back(quadCount + 1);
         }

         indices.push_back(quadCount + divisions);

         // 2nd Triangle
         if(i == divisions - 1)
         {
            // Wrap to the first edge between the rings
            indices.push_back(quadCount - divisions + 1);
            indices.push_back(quadCount + 1);
            indices.push_back(quadCount + divisions);
         }
         else
         {
            indices.push_back(quadCount + 1);
            indices.push_back(quadCount + divisions + 1);
            indices.push_back(quadCount + divisions);
         }

         ++quadCount;
      }
   }

   // Negative Y cap
   for(unsigned i = numPoints - 1 - divisions; i < numPoints - 1; ++i)
   {
      indices.push_back(numPoints - 1);
      indices.push_back(i);

      if(i == numPoints - 2)
      {
         // Wrap to first vertex in the ring
         indices.push_back(numPoints - 1 - divisions);
      }
      else
      {
         indices.push_back(i + 1);
      }
   }

   // Create the index and vertex buffers
   Buffer::SharedPtr bufPositions(new Buffer(device, POSITION, positions));
   buffers.push_back(bufPositions);

   Buffer::SharedPtr bufTexCoords(new Buffer(device, TEXCOORD2D, texCoords));
   buffers.push_back(bufTexCoords);

   Buffer::SharedPtr bufNormals(new Buffer(device, NORMAL, positions));
   buffers.push_back(bufNormals);

   Buffer::SharedPtr bufIndices(new Buffer(device, INDEX, indices));
   buffers.push_back(bufIndices);
}

[Edited by - brekehan on June 5, 2009 1:36:54 PM]

Share this post


Link to post
Share on other sites
I haven't gone through all of your code yet, but you should be able to map the texture coordinates in the range [0,1] to the rho and phi values respectively. It sounds like your generated texture coordinates are mapped in a double range, hence making the texture appear twice on the object.

Share this post


Link to post
Share on other sites
I realise this is an old thread, but that tutorial for spherical texture coordinates still ranks high on Google, so I thought I'd post an answer to help others in future.

That tutorial ([url="http://www.mvps.org/directx/articles/spheremap.htm"]http://www.mvps.org/...s/spheremap.htm[/url]) does indeed draw the texture twice on the sphere (once forwards, once backwards). You can see that this will happen as it doesn't consider the z coordinate of the normal in either of the formulas, so it will always produce symmetrical results.

I found a better solution here:
[url="http://www.cse.msu.edu/%7Ecse872/tutorial4.html"]http://www.cse.msu.e.../tutorial4.html[/url]

[code]
texCoord.x = atan2(normal.x, normal.z) / (2.0 * MathUtility::PI) + 0.5;
texCoord.y = asin(normal.y) / MathUtility::PI + 0.5;
[/code]

The y texture coordinate is the same as the first tutorial, but the x texture coordinate uses the z normal value too.

If you find yourself getting strange results in the texture along the zero longitude seam of the sphere, this is probably because you are using the same texture coordinates for those vertices, so it is only mapping to one side of the texture. You may need to generate new texture coordinates (thus perhaps new vertices) for the 'closing' seam of the sphere, so they can be mapped to the other side of the texture.

You may also have the same problem at the 'poles' if there is a single vertex, with a single set of texture coordinates, causing the texture to warp.

Regards,
Dave

Share this post


Link to post
Share on other sites

This topic is 2345 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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