1

Can someone please tell me which part of the code I'm messing up?

In glDrawElements, I got like only 1/3 of the sphere with "indices.size() / 3" , which was how most tutorial was conducted, but when I tried indices.size(), I got segmentation fault and nothing showed up.

vector<GLfloat> vertices, normals, texCoords;
vector<GLushort> indices;

void setupSphere(double r, int slice, int stack)
{
    vertices.resize(slice * stack * 3);
    normals.resize(slice * stack * 3);
    texCoords.resize(slice * stack * 2);

    float x, y, z, xz;
    float nx, ny, nz, lengthInv = 1.0f / r;
    float s, t;

    float sliceAngle, stackAngle;

    vector<GLfloat>::iterator itV = vertices.begin();
    vector<GLfloat>::iterator itN = normals.begin();
    vector<GLfloat>::iterator itT = texCoords.begin();

    for (int i = 0; i < stack; i++)
    {
        stackAngle = M_PI_2 - M_PI * i / stack;
        xz = r * cosf(stackAngle);
        y = r * sinf(stackAngle);

        for (int j = 0; j < slice; j++)
        {
            sliceAngle = 2 * M_PI * j / slice;

            x = xz * sinf(sliceAngle);
            z = xz * cosf(sliceAngle);
            *itV++ = x;
            *itV++ = y;
            *itV++ = z;

            nx = x * lengthInv;
            ny = y * lengthInv;
            nz = z * lengthInv;
            *itN++ = nx;
            *itN++ = ny;
            *itN++ = nz;

            s = (float)j / slice;
            t = (float)i / stack;
            *itT++ = s;
            *itT++ = t;
        }
    }

    int first, second;
    for (int i = 0; i < stack; i++)
    {
        for (int j = 0; j < slice; j++)
        {
            first = i * slice + j;
            second = first + slice + 1;

            indices.push_back(first);
            indices.push_back(second);
            indices.push_back(first + 1);

            indices.push_back(first + 1);
            indices.push_back(second);
            indices.push_back(second + 1);
        }
    }
}

void drawSphere()
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
    glNormalPointer(GL_FLOAT, 0, &normals[0]);
    glTexCoordPointer(2, GL_FLOAT, 0, &texCoords[0]);

    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
}
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Brothre23
  • 39
  • 1
  • 8
  • Your sphere looks too smooth. How many vertices do you have? – lvella Oct 08 '20 at 09:54
  • I’m going with 360 for longitude and 180 for latitude, but the number of vertices seemed to have little influence on the smoothness of the sphere, which I don’t understand why. – Brothre23 Oct 08 '20 at 10:01
  • Then you have 194400 vertices, way more than what can be indexed by `GLushort`. See my answer. – lvella Oct 08 '20 at 10:05

2 Answers2

1

You generate N stacks and M slices. Hence you can just create (N-1)x(M-1) quads.
The indices of the quads are (first, first+slice, first+1, first+slice+1), where first is the first index of the quad:

for (int i = 0; i < stack-1; i++)
{
    for (int j = 0; j < slice-1; j++)
    {
        int first = i*slice + j;
        unsigned short qi[]{first, first+slice, first+1, first+slice+1};

        indices.insert(indices.end(), {qi[0], qi[2], qi[3], qi[0], qi[3], qi[1]});
    }
}

See the answer to the questions Sphere Calculations and How to map texture to sphere that rendered by parametric equation using points primitive.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • `*itV++ = x;` is indeed the same as `*itV = x; itV++`, except for sequence points, which is not an issue here unless the operators= and * are overloaded and have side effects. – lvella Oct 08 '20 at 09:30
  • @lvella You're right. Side note, the _count_ argument of [`glDrawElements`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElements.xhtml) specifies the number of indices, not the number of [primitives](https://www.khronos.org/opengl/wiki/Primitive). – Rabbid76 Oct 08 '20 at 15:16
1

The correct is indeed indices.size() / 3, because you must pass the number of triangles, not the number of indices.

The correct is indeed indices.size(), because you must pass the number of elements in the array you are passing. I don't know why the tutorials you saw used / 3 variation.

As to the problem of the missing sphere section, my best guess with the provided information is that you have more than 65535 vertices, so the indices wraps around that value, because you are using GL_UNSIGNED_SHORT.

Try changing from vector<GLushort> indices; to vector<GLuint> indices; and using GL_UNSIGNED_INT in glDrawElements().

Alternatively, lower the values of slice and stack so that slice * stack * 3 <= 65535.

lvella
  • 12,754
  • 11
  • 54
  • 106
  • Oh, that was right! Sorry for forgetting those int vs short things. But indices.size() / 3 still only draws 1/3 of the sphere, whereas indices.size() gives a full one, any idea? – Brothre23 Oct 08 '20 at 10:12
  • Maybe I was wrong interpreting the [documentation for `glDrawElements()`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElements.xhtml). I interpreted "elements" as "primitives", but maybe it really means "indices", and then the correct is `indices.size()`. – lvella Oct 08 '20 at 10:14