Sphere Math

Started by
8 comments, last by cbastien 15 years, 9 months ago
I have been trying to find an approach to create a sphere using stacks and slices. Does anyone know how to do this?
Advertisement
Quote:Original post by Underwood
I have been trying to find an approach to create a sphere using stacks and slices. Does anyone know how to do this?
This is pretty well documented online. Try Googling (or searching the GDNet archives for) phrases such as 'sphere mesh', and you should be able to find sample code.

In short, you'll want to use spherical coordinates and two (nested) loops, one each for the two angles involved (longitude and latitude, essentially).
I read a few ways to create the points (like how you mentioned), however, I am just unsure on how to create the indicies. It also seems as a few techniques create two points in a second loop of the nested loop and some create only one.
ii think the easiest way for me is to draw a cylinder (just like a regular gride but you wrap the last index around) and then you "squash" the two ends together. more points at the top and bottom than a sphere, but muhc simpler! :P
Hi, here is my function that I use to create a sphere. Maybe it will be helpful to you. The structure VERTEX looks like this:
struct VERTEX{	D3DXVECTOR3 pos;	D3DXVECTOR3 n;    D3DXVECTOR2 uv;};

BOOL CreateSphere(Mesh *pMesh, float Radius, UINT Slices, UINT Stacks){    pMesh->NumVertices = Slices*(Stacks-1)+2;    pMesh->NumIndices = Slices*2*3*(Stacks-2)+Slices*3*2;        pMesh->pVertices = new VERTEX[pMesh->NumVertices];    pMesh->pIndices = new WORD[pMesh->NumIndices];    float x,y,z,r;    float fStackAngle = 3.141592f / (float)Stacks;    float fSliceAngle = 2*3.141592f / (float)Slices;    // generate vertices    VERTEX *pV = pMesh->pVertices;    pV->pos = D3DXVECTOR3(0,Radius,0);    pV++;    for (unsigned int i = 1; i < Stacks; i++)    {        y = cosf(fStackAngle*(float)i)*Radius;        r = sinf(fStackAngle*(float)i)*Radius;        for (unsigned int k = 0; k < Slices; k++)        {            x = cosf(fSliceAngle*(float)k)*r;            z = sinf(fSliceAngle*(float)k)*r;            pV->pos = D3DXVECTOR3(x,y,z);            pV++;        }    }    pV->pos = D3DXVECTOR3(0,-Radius,0);    pV++;    // generate indices    WORD *pI = pMesh->pIndices;    for (unsigned int i = 1; i < Slices; i++)    {        *pI++ = 0;        *pI++ = i+1;        *pI++ = i;    }    *pI++ = 0;    *pI++ = 1;    *pI++ = Slices;    int sv = 1;    for (unsigned int i = 0; i < Stacks-2; i++)    {        for (unsigned int k = 0; k < Slices-1; k++)        {            *pI++ = sv+k;            *pI++ = sv+k+1;            *pI++ = sv+k+Slices;            *pI++ = sv+k+1;            *pI++ = sv+k+1+Slices;            *pI++ = sv+k+Slices;        }        *pI++ = sv+Slices-1;        *pI++ = sv;        *pI++ = sv+Slices+Slices-1;        *pI++ = sv;        *pI++ = sv+Slices;        *pI++ = sv+Slices+Slices-1;        sv += Slices;    }    for (unsigned int i = 0; i < Slices-1; i++)    {        *pI++ = pMesh->NumVertices-1;        *pI++ = sv+i;        *pI++ = sv+i+1;    }    *pI++ = pMesh->NumVertices-1;    *pI++ = sv+Slices-1;    *pI++ = sv;    // generate normals    for (unsigned int i = 0; i < pMesh->NumVertices; i++)    {        // pos is a vector from the origin to the vertex, this can be used as the normal        D3DXVec3Normalize(&pMesh->pVertices.n, &pMesh->pVertices.pos);    }    // generate texture coordinates    for (DWORD i = 0; i < pMesh->NumVertices; i++) 	{        pMesh->pVertices.uv.x = 0.0f+(asinf(pMesh->pVertices.n.x)/(float)D3DX_PI+0.5f);        pMesh->pVertices.uv.y = 1.0f-(asinf(pMesh->pVertices.n.y)/(float)D3DX_PI+0.5f);    }    return TRUE;}
84r: That is very helpful, I am going to definitely be taking a look at it. I was wondering if you could explain some of the mathimatical reasoning for doing it the way you coded it?
First you want to know the number of vertices and indices that your sphere will have. Lets say we want 4 stacks and 8 slices then we expect this:
img1
There are 2 outer vertices (at the top and at the bottom of the sphere) and 3 inner rings (blue arrows). Each ring has 8 vertices because we want 8 slices. The total number of vertices is then 2+(4-1)*8 (2 outer vertices + number of inner rings * number of stacks) or in general 2+(Stacks-1)*Slices. Next we want to figure out the number of indices because we want to use an indexed triangle list. Drawing another picture will be helpful.
img2
You see that for the outer stacks (top stack and bottom stack) you need 8 triangles per stack. In the inner stacks the number of triangles doubles, because here you have 8 quads, with 2 triangles each. The total number of indices is [number of triangles for the 2 outer stacks + number of triangles for the inner stacks (number inner stacks = number of all stacks - 2 outer stacks)]*(number indices per triangle = 3) = [2*Slices+(Stacks-2)*(Slices*2)]*3. We need two more parameters before it gets really messy.

One of those parameters is the height of each ring (Y-Position) where the vertices will be placed.
img3
We device pi (angle to go from the top vertex to the bottom vertex) by the number of stacks to get theta. theta is the angle you need to go to get to the next stack ring. So that the Y-coordinate of the first inner ring will be cos(theta)*Radius. The height of the second ring cos(2*theta)*Radius, third circle = cos(3*theta)*Radius, etc..
float fStackAngle = theta = 3.141592f / (float)Stacks;
The radius of a ring in the XZ plane is sin(i*theta)*Radius, where i = 1,2,3,....

Similarely we get the X and Z position for the vertices. We assume the X axis is where the angle is zero.
img4
For example in the above image what is the X and Z coordinates for the red dot? X = cos(3*phi)*Radius, Z = sin(3*phi)*Radius.
float fSliceAngle = phi = 2*3.141592f / (float)Slices;

That was the easiest part, now comes the hard part.

Generating vertices

// First add the top vertex (this is easy)    pV->pos = D3DXVECTOR3(0,Radius,0);    pV++;    // Then generate vertices for the inner rings. For that loop through all rings (number of rings = Stacks-2, that's why the loop starts at 1 and ends at Stacks-1)...    for (unsigned int i = 1; i < Stacks; i++)    {        // ...get the y position for the current ring and it's radius on the XZ plane.        y = cosf(fStackAngle*(float)i)*Radius;        r = sinf(fStackAngle*(float)i)*Radius;        Then generate a vertex for every slice in the ring.        for (unsigned int k = 0; k < Slices; k++)        {            x = cosf(fSliceAngle*(float)k)*r;            z = sinf(fSliceAngle*(float)k)*r;            pV->pos = D3DXVECTOR3(x,y,z);            pV++;        }    }    // Finally add the bottom vertex (0,-Radius, 0).    pV->pos = D3DXVECTOR3(0,-Radius,0);

Index generation is a little bit more complicated. Here's the image of the top stack, the numbers indicate the index of a vertex in the vertex array.
img5
    // generate indices for the top stack. need 3 indices for a triangle.    for (unsigned int i = 1; i < Slices; i++)    {        // the pattern in the above image is:        // (0,2,1), (0,3,2), (0,4,3), (0,5,4), ... 1 triangle is not included in this loop        *pI++ = 0;        *pI++ = i+1;        *pI++ = i;    }    // last triangle is (0,1,8)    *pI++ = 0;    *pI++ = 1;    *pI++ = Slices;

Now to the inner stacks:
img6
    int sv = 1; // sv is the index of the first vertex in the current stack.    // 2 triangles per quad: the pattern for the first stack is:    // (1,2,9)(2,10,9), (2,3,10)(3,11,10), (3,4,11)(4,12,11), ...    for (unsigned int i = 0; i < Stacks-2; i++)    {        for (unsigned int k = 0; k < Slices-1; k++)        {            *pI++ = sv+k;        // 1, 2, 3, 4, ...            *pI++ = sv+k+1;      // 2, 3, 4, 5, ...            *pI++ = sv+k+Slices; // 9,10,11,12, ...            *pI++ = sv+k+1;        //  2, 3, 4, ...            *pI++ = sv+k+1+Slices; // 10,11,12, ...            *pI++ = sv+k+Slices;   //  9,10,11, ...        }        // the last quad must be set manually        // (8,1,16)        *pI++ = sv+Slices-1;        *pI++ = sv;        *pI++ = sv+Slices+Slices-1;        // (1,9,16)        *pI++ = sv;        *pI++ = sv+Slices;        *pI++ = sv+Slices+Slices-1;        sv += Slices;    }

    // Bottom stack: same procedure as with the top stack. but careful with CW, CCW.    for (unsigned int i = 0; i < Slices-1; i++)    {        *pI++ = pMesh->NumVertices-1;        *pI++ = sv+i;        *pI++ = sv+i+1;    }    // one triangle in the bottom stack needs to be done manually.    *pI++ = pMesh->NumVertices-1;    *pI++ = sv+Slices-1;    *pI++ = sv;

Texture coordinates (uv):
the v component should be 0.0f for the top vertex and 1.0f for the bottom vertex. The values for the vertices inbetween are interpolated. But not an interpolation along the Y axis, but along the circumference that's why arcsin is used. The u coordinate in my code is 0.0f for vertices with normal.x = -1, 0.5f for vertices with normal.x = 0 and 1.0f for vertices with normal.x = 1. I guess there are better ways to get the u coordinate.
Quote:Original post by 84r
First you want to know the number of vertices and indices...


That would make a wicked tutorial. Rate++. Great work!



Thanks taby!
a really good link to help you to make a sphere:

http://local.wasp.uwa.edu.au/~pbourke/texture_colour/spheremap/


it explains you how to :

- create a sphere
- create a subset of a sphere
- texture it
- etc...


seb.

This topic is closed to new replies.

Advertisement