1
\$\begingroup\$

I am trying to implement a class Sphere in C++.

Therefore I want to calculate the vertices in the constructor of the class (or in a seperate function..). Although I read tons of articles about creating spheres in different ways (UV Sphere, Quad Sphere, Icosphere etc.) I did not understand how to create the vertices for my buffer object.

I decided to use the UV Sphere since it is easy to map a texture on it.

A "UV sphere" in this sense is one where the (quad) edges run like lines of latitude and longitude, and the texture is mapped to the sphere like an equirectangular projection.

Example of a UV sphere and its texture
(Equirectangular Earth texture by Strebe via Wikimedia Commons, CC BY-SA 3.0)

But how can I calculate all the vertex positions and texture coordinates?

For my vertices I use a Vertex struct:

struct Vertex
{
    glm::vec3 position;
    glm::vec2 texture;
};
\$\endgroup\$
1
  • 2
    \$\begingroup\$ It can be tempting to compute this sort of stuff on the fly, but honestly, it's easier to just download a texture mapped obj model for a sphere. That way you spend less time on unnecessary algorithms and more on actually making games. \$\endgroup\$
    – Ian Young
    Commented Nov 1, 2017 at 9:20

1 Answer 1

4
\$\begingroup\$

Let's start by generating all unique vertices. You can then decide whether to index those vertices, or copy them into strips/fans, depending on your needs. Here's some example code showing how to organize these vertices into triangles for rendering.

// One vertex at every latitude-longitude intersection,
// plus one for the north pole and one for the south.
// One meridian serves as a UV seam, so we double the vertices there.
int numVertices = numLatitudeLines * (numLongitudeLines + 1) + 2

vec3[] positions = vec3[numVertices]
vec2[] texcoords = vec2[numVertices]

// North pole.
positions[0] = vec3(0, radius, 0)
texcoords[0] = vec2(0, 1)

// South pole.
positions[numVertices - 1] = vec3(0, -radius, 0)
texcoords[numVertices - 1] = vec2(0, 0)

// +1.0f because there's a gap between the poles and the first parallel.
float latitudeSpacing = 1.0f / (numLatitudeLines + 1.0f)
float longitudeSpacing = 1.0f / (numLongitudeLines)

// start writing new vertices at position 1
int v = 1
for(latitude = 0; latitude < numLatitudeLines; latitude++) {
    for(longitude = 0; longitude <= numLongitudeLines; longitude++) {

      // Scale coordinates into the 0...1 texture coordinate range,
      // with north at the top (y = 1).
      texcoords[v] = vec2(
                        longitude * longitudeSpacing,
                        1.0f - (latitude + 1) * latitudeSpacing                            
                     )

      // Convert to spherical coordinates:
      // theta is a longitude angle (around the equator) in radians.
      // phi is a latitude angle (north or south of the equator).
      float theta = texcoords[v].x * 2.0f * PI
      float phi = (texcoords[v].y - 0.5f) * PI

      // This determines the radius of the ring of this line of latitude.
      // It's widest at the equator, and narrows as phi increases/decreases.
      float c = cos(phi)

      // Usual formula for a vector in spherical coordinates.
      // You can exchange x & z to wind the opposite way around the sphere.
      positions[v] = vec3(
                        c * cos(theta),
                        sin(phi),
                        c * sin(theta)
                      ) * radius
      
      // Proceed to the next vertex.
      v++
    }
}

This puts the north Pole at position 0 in the array, then the vertices of each line of latitude in a row (with the first and last vertices in the row being coincident along the texturing seam), one line after another until we reach the south pole at the end of the array.

We've arbitrarily made just one vertex for each pole, all the way on the left side of the texture. You could also choose to make multiple pole vertices to make the texture distortion less extreme, but you'll always have some seams or stretching here due to the nonlinear nature of equirectangular mapping.

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .