0
votes

I'm learning OpenGL and I'm working on creating my own sphere model. I was able to draw a complete sphere, although with some puzzling results. I'm wondering if someone can explain (and possibly correct) my code.

The rationale: build triangles using carthesian coordinates calculated from polar coordinates. The number of subdivisions tells me the steps in phi or theta radians to generate the sphere point. From a particular point P(phi, theta), I build the other edges of the sector for [phi, delta_phi], [theta, delta_tetha], with phi varying from [0, pi] (180 degrees) and tetha from [0, 2*pi] (360 degrees).

This is the code I came up with (I'm using QT objects, but it should be pretty straitghforward):

QVector3D polarToCarthesian(float rho, float phi, float theta)
{
    float r = qSin(phi) * rho;
    float y = qCos(phi) * rho;

    float x = r * qSin(theta);
    float z = r * qCos(theta);

    return QVector3D{x, y, z};
}

void make_sector(QVector<QVector3D>& mesh, float phi, float theta, float rho, float deltaPhi, float deltaTheta)
{
    QVector3D p1 = polarToCarthesian(rho, phi, theta);
    QVector3D p2 = polarToCarthesian(rho, phi, theta + deltaTheta);
    QVector3D p3 = polarToCarthesian(rho, phi + deltaPhi, theta);
    QVector3D p4 = polarToCarthesian(rho, phi + deltaPhi, theta + deltaTheta);

    // First Triangle
    mesh.push_back(p1);
    mesh.push_back(p1); // Normal
    mesh.push_back(p3);
    mesh.push_back(p3); // Normal
    mesh.push_back(p2);
    mesh.push_back(p2); // Normal

    // Second Triangle
    mesh.push_back(p2);
    mesh.push_back(p2); // Normal
    mesh.push_back(p3);
    mesh.push_back(p3); // Normal
    mesh.push_back(p4);
    mesh.push_back(p4); // Normal
}

void build_sphere(QVector<QVector3D>& mesh, int ndiv)
{
    const float PHI_MAX = static_cast<float>(M_PI);
    const float THETA_MAX = static_cast<float>(M_PI) * 2;

    const float delta_phi = PHI_MAX / ndiv;
    const float delta_theta = THETA_MAX / ndiv;

    for (int i = 0; i < ndiv; ++i) {
        float phi = i * delta_phi;
        for (int j = 0; j < ndiv; ++j) {
            float theta = j * delta_theta;
            make_sector(mesh, phi, theta, 1.0f, delta_phi, delta_theta);
        }
    }
}

// Then I can generate the sphere with
build_sphere(sphere_mesh, 10);

However, I cannot get a complete sphere unless I change the iteration for phi from ndiv iterations to 3 * ndiv iterations. I don't understand why! Phi should vary from 0 to PI to cover the whole Y axis while Theta from 0 to 2 * pi should cover the XZ plane.

Can somebody explain what's happening and why 3 * ndiv works?

1
How are you drawing the whole thing? I have a strong suspicion that you're only drawing 1/3 of all the vertices you generate. Then, if you generate 3 times the necessary vertices, 1/3 of those vertices are enough to cover the whole sphere. With anything less, parts are missing.Reto Koradi
I load the data to a vertex buffer object, and draw it using glDrawArrays(GL_TRIANGLES, 0, mesh.size() / 2) (that's half because the normal values end up taking half of the vector). I bind the vertex values with glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0) and the normal values with glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 3 * sizeof(GLfloat));Homero C. de Almeida
@RetoKoradi After rewriting the software in another computer to take screenshots, I found the bug. The problem was that I loaded only a third of the model in the vertex buffer because I used glBufferData with a size of model.size() * sizeof(GLfloat) instead of model.size() * sizeof(QVector3D). Now it's working as expected and I got a full sphere. :)Homero C. de Almeida
Although you might get your code working somehow, I think you'd find greater satisfaction implementing the "icosphere" method described in this blog post: blog.andreaskahler.com/2009/06/…. That saved me a lot of headaches.Rethunk

1 Answers

0
votes

phi should go from -π/2 to +π/2, not from 0 to π:

float phi = i * delta_phi - (M_PI / 2);

Also, you appear to have your r and y calculations the wrong way around. You want r to be maximum at the equator (when phi == 0).

I think your code may have worked (albeit producing twice as many polygons as it should have) if you had stuck at 2 * ndiv. As it is, going from 0 to π only puts polygons in the northern hemisphere, so you have to keep going beyond that to have any polygons in the southern hemisphere.

p.s. there's no 'h' in cartesian ;)