3

I am relatively new to OpenGL and C++ in general and I've been working on a custom framework in which I am asked to render spheres. I ve done research and managed to generate a somewhat-sphere made from points. I would like if possible to reform it so I can have an actual sphere made from triangles having properties such as radius. The code I used to generate sphere made from points is as follows:

Mesh* Mesh::GenerateSphere()
{

    const int Y_SEGMENT= 10;
    const int X_SEGMENT = 20;
    //const float count;


    Mesh*m = new Mesh();
    m->numVertices = (X_SEGMENT+1)*(Y_SEGMENT+1);
    m->type = GL_POINTS;
    m->vertices = new Vector3[m->numVertices];

    //s->GenerateTriangle();
    for (int i = 0; i < Y_SEGMENT+1;i++)
    {
        float angleY = PI*i / Y_SEGMENT;
        float y = cos(angleY);
        float xz = sin(angleY);

        for (int j = 0; j < X_SEGMENT+1; j++)
        {

            float angleX = 2*PI*j / X_SEGMENT;
            float x = xz*cos(angleX);
            float z = xz*sin(angleX);
            Vector3 v(x,y,z);
            m->vertices[i * (X_SEGMENT+1)+j] = v;
        }
    }
    m->BufferData();
    return m;
}
Jim Rod
  • 41
  • 1
  • 5
  • Have you looked at https://www.opengl.org/resources/libraries/glut/spec3/node81.html – Lucian Mar 02 '15 at 10:08
  • 1
    You can get a "smoother" sphere by subdividing an icosahedron. (An *actual* sphere isn't made from triangles, though.) – molbdnilo Mar 02 '15 at 10:10
  • here is an implementation of sphere in free glut project: http://sourceforge.net/p/freeglut/code/HEAD/tree/trunk/freeglut/freeglut/src/fg_geometry.c#l8 – Lucian Mar 02 '15 at 10:13
  • Due to the framework's layout I can't use glut – Jim Rod Mar 02 '15 at 10:55
  • I don't think it's a duplicate since the answer on the post relies on the use of glu/glut which I can't use. – Jim Rod Mar 02 '15 at 11:40
  • @JimRod: _Off Topic:_ Do you realize that your for loops mean that you'll iterate 11 times and 21 times ( 0 - 10 inclusive and 0 - 20 inclusive)? – enhzflep Mar 02 '15 at 12:44
  • A few more questions/answers on rendering spheres: http://stackoverflow.com/q/24137198/3530129, http://stackoverflow.com/q/26790422/3530129. – Reto Koradi Mar 03 '15 at 02:11

2 Answers2

1

Here's some code I wrote a long, long time ago. I've drawn the areas immediately surrounding the poles with a triangle-fan and the remainder of the sphere with quad-strips. You could of course, use triangles instead of the quads, but since the pairs of triangles would still be planar, it wont look any different, unless I'm mistaken - it's been a long times since I touched anything GL.

As molbdnilo points out, you'll get a better sphere by calculating your points in a different manner. If the intention is to texture-map the sphere, you'll get better results again if you subdivide and smooth a cube, since this avoids 'pinching' around the poles. Here's a good article that discusses this: http://www.iquilezles.org/www/articles/patchedsphere/patchedsphere.htm

I should also point-out that there's a problem with the way that I either calculate the normals, or transform then when rotating - I used to get funky lighting results when looking at the sphere when it was rotated. (I think it's with the normals) Also, looking at the just code now, I'm not sure that I've computed the number of vertices required correctly - you'll have to double-check that.It appears as though I don't store verts for either of the poles in the array.

EDIT: Here's a pic of the output:

enter image description here

typedef struct {
    GLfloat x, y, z;
}vec3;

void myGlutBall(float radius, int numStacks, int numSides)
{
//    vec3 points[sides * (sides-1)];
    GLfloat curRadius, curTheta, curRho, deltaTheta, deltaRho, curX,curY,curZ;
    int curStack, curSlice, numVerts = (numStacks-1)*numSides;
    vec3 points[numVerts];
    int curVert = 0;
    int t;

    deltaTheta = (2*M_PI) / numSides;
    deltaRho = M_PI / numStacks;

        for (curStack=1; curStack<numStacks; curStack++)
        {
            curRho = (3.141/2.0) - curStack*deltaRho;
            curY = sin(curRho) * radius;
            curRadius = cos(curRho) * radius;
            for (curSlice=0; curSlice<numSides; curSlice++)
            {
                curTheta = curSlice * deltaTheta;
                curX = curRadius * cos(curTheta);
                curZ = -curRadius * sin(curTheta);
                points[curVert++] = vec3{curX,curY,curZ};
            }
        }

    // option 1 - points only
    /*
    glBegin(GL_POINTS);
    glNormal3d(0,1,0);
    glVertex3d(0,radius,0);
    for (t=0; t<numVerts; t++)
    {
        curX = points[t].x;
        curY = points[t].y;
        curZ = points[t].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    }
    glNormal3d(0,-1,0);
    glVertex3d(0,-radius,0);
    glEnd();
    */

    ///////////////////////////////
    // option 2 - solid
    ///////////////////////////////
    // part A - draw the top 'lid' (tris)
    glBegin(GL_TRIANGLE_FAN);
        glNormal3d(0,1,0);
        glVertex3d(0,radius,0);
        for (t=0; t<numSides; t++)
        {
            curX = points[t].x;
            curY = points[t].y;
            curZ = points[t].z;
            glNormal3d(curX, curY, curZ);
            glVertex3d(curX, curY, curZ);
        }
            curX = points[0].x;
            curY = points[0].y;
            curZ = points[0].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    glEnd();

    // part B - draw the 'sides' (quads)
    int vertIndex;
    for (curStack=0; curStack<numStacks-2; curStack++)
    {
        vertIndex = curStack * numSides;
        glBegin(GL_QUAD_STRIP);
            for (curSlice=0; curSlice<numSides; curSlice++)
            {
                glNormal3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z);
                glVertex3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z);

                glNormal3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z);
                glVertex3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z);
            }
            glNormal3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z);
            glVertex3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z);
            glNormal3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z);
            glVertex3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z);
        glEnd();
    }

    // part C - draw the bottom 'lid' (tris)
    glBegin(GL_TRIANGLE_FAN);
        glNormal3d(0,-1,0);
        glVertex3d(0,-radius,0);
        for (t=0; t<numSides-1; t++)
        {
            curX = points[numVerts-1-t].x;
            curY = points[numVerts-1-t].y;
            curZ = points[numVerts-1-t].z;
            glNormal3d(curX, curY, curZ);
            glVertex3d(curX, curY, curZ);
        }
            curX = points[numVerts-1].x;
            curY = points[numVerts-1].y;
            curZ = points[numVerts-1].z;
        glNormal3d(curX, curY, curZ);
        glVertex3d(curX, curY, curZ);
    glEnd();

}
enhzflep
  • 12,927
  • 2
  • 32
  • 51
1

In my rendering class I was taught to imagine a Sphere as a rounded grid. So first you take the implementation of a grid with dimension 1 in x and y, centered in the position (0,0,0) that will be subdivided by n-rows (rowMax) and m-columns (colMax):

// Aux function
inline int index (int i, int j)
{
    return i + j*(m_colMax + 1);
}

float numCoords = 3*rowMax*colMax; // Array size

float *coordData = new float[numCoords]; // Array with coordinate positions

// Fill coordinate positions [to change]
for (int j = 0; j <= rowMax; j++) {
    for (int i = 0; i <= colMax; i++) {
        int k = index(i, j);
        coordData[k] = (float)i / m_colMax - (0.5f);
        coordData[k + 1] = (float)j / m_rowMax - (0.5f);
        coordData[k + 2] = 0;
    }
}

// Fill index
int k = 0;
GLuint *indexData = new GLuint[numCoords]; // Array with indexing data 
for (int j = 0; j < rowMax; j++) {
    for (int i = 0; i < colMax; i++) {
        indexData[k++] = index (i, j);
        indexData[k++] = index (i + 1, j + 1);
        indexData[k++] = index (i, j + 1);
        indexData[k++] = index (i, j);
        indexData[k++] = index (i + 1, j);
        indexData[k++] = index (i + 1, j + 1);          
    }
}

And with this data, remember to use DrawElements() and GL_TRIANGLES to take indexing into account (the second link has a clear image of this approach). Since you are new to OpenGL, this two links can summarize what you need to learn:

[1] Using OGL 2.1 without shaders: http://www.songho.ca/opengl/gl_vertexarray.html

[2] Using more advanced techniques (aka, OGL 3.3+ with Core/Compatibility Profile) http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/

Now, to do what you need, just change the code in the first for loop:

// Fill coordinate positions 
// Pi variable can be a define or declared somewhere in your code
float radius = 1.0f;
for (int j = 0; j <= rowMax; j++) {
    for (int i = 0; i <= colMax; i++) {
        int k = index(i, j);
        float teta = ((float)i / m_colMax) * 2 * (float)Pi;
        float fi = ((float)j / m_rowMax)*(float)Pi;
        coordData[k] = radius*(cos (teta))*(sin (fi));
        coordData[k + 1] = radius*(cos (fi));
        coordData[k + 2] = 1.0f*(sin (teta))*(sin (fi));
    }
}

And you will have your sphere coordinates with indexes. Use a for loop to draw it on the old pipeline of OpenGL (2.1 or Compatibility Profile) or setup your buffers (VAO, VBO) on the new pipeline of OpenGL (Core Profile).

lfn
  • 114
  • 1
  • 5