Generating a sphere in OpenGL without high level libraries - what's wrong with my code?

150 views Asked by At

I've tried to implement an inefficient function to generate the points, normals, tex coords, and index list of a sphere.

Ignoring the lines, when I draw the sphere with OpenGL I get the following output which is clearly wrong:

enter image description here

Can anyone help me understand what's wrong with my code?

public SphereObject()
{
    super();

    // inefficient but quick sphere data

    int num_points = 16;
    double as = Math.PI / num_points;
    double theta, phi;
    double [] p;

    ArrayList<double []> points = new ArrayList<double []>();
    ArrayList<Integer> edges = new ArrayList<Integer>();
    ArrayList<double []> normals = new ArrayList<double []>();
    ArrayList<double []> tex = new ArrayList<double []>();

    theta = Math.PI;
    phi = Math.PI / 2;

    for(int row = 0; row < num_points; row++)
    {
        for(int col = 0; col < num_points; col++)
        {
            p = new double[3];
            p[0] = Math.sin(theta) * Math.cos(phi - as);
            p[1] = Math.cos(theta) * Math.cos(phi - as);
            p[2] = Math.sin(phi - as);
            points.add(p);
            normals.add(p);
            tex.add(new double [] {0, 0});

            p = new double[3];
            p[0] = Math.sin(theta + 2 * as) * Math.cos(phi - as);
            p[1] = Math.cos(theta + 2 * as) * Math.cos(phi - as);
            p[2] = Math.sin(phi - as);
            points.add(p);
            normals.add(p);
            tex.add(new double [] {1, 0});

            p = new double[3];
            p[0] = Math.sin(theta + 2 * as) * Math.cos(phi);
            p[1] = Math.cos(theta + 2 * as) * Math.cos(phi);
            p[2] = Math.sin(phi);
            points.add(p);
            normals.add(p);
            tex.add(new double [] {1, 1});

            p = new double[3];
            p[0] = Math.sin(theta) * Math.cos(phi);
            p[1] = Math.cos(theta) * Math.cos(phi);
            p[2] = Math.sin(phi);
            points.add(p);
            normals.add(p);
            tex.add(new double [] {0, 1});

            // make triangles
            edges.add(points.size()-1);
            edges.add(points.size()-3);
            edges.add(points.size()-4);

            edges.add(points.size()-1);
            edges.add(points.size()-2);
            edges.add(points.size()-3);

            theta -= 2 * as;
        }

        phi -= as;
    }

    sphereVertices = new double[points.size() * 3];
    sphereTexcoords = new double[tex.size() * 2];
    sphereNormals = new double[normals.size() * 3];
    sphereIndices = new short[edges.size() * 1];

    for(int c1 = 0; c1 < points.size(); c1 += 3)
    {
        sphereVertices[c1] = points.get(c1)[0];
        sphereVertices[c1+1] = points.get(c1)[1];
        sphereVertices[c1+2] = points.get(c1)[2];
    }

    for(int c1 = 0; c1 < tex.size(); c1 += 2)
    {
        sphereTexcoords[c1] = tex.get(c1)[0];
        sphereTexcoords[c1+1] = tex.get(c1)[1];
    }

    for(int c1 = 0; c1 < normals.size(); c1 += 3)
    {
        sphereNormals[c1] = normals.get(c1)[0];
        sphereNormals[c1+1] = normals.get(c1)[1];
        sphereNormals[c1+2] = normals.get(c1)[2];
    }

    for(int c1 = 0; c1 < edges.size(); c1++)
    {
        sphereIndices[c1] = edges.get(c1).shortValue();
    }

    mVertBuff = fillBuffer(sphereVertices);
    mTexCoordBuff = fillBuffer(sphereTexcoords);
    mNormBuff = fillBuffer(sphereNormals);
    mIndBuff = fillBuffer(sphereIndices);
}

My OpenGL code is below. The getVertices() functions et al return the buffers created in the Sphere constructor above.

    Matrix.translateM(modelViewMatrix, 0, 0, 0, kObjectScale);
    Matrix.scaleM(modelViewMatrix, 0, kObjectScale, kObjectScale, kObjectScale);

    GLES20.glUseProgram(shaderProgramID);

    GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getVertices());
    GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getNormals());
    GLES20.glVertexAttribPointer(textureCoordHandle, 2, GLES20.GL_FLOAT, false, 0, sphere.getInstance().getTexCoords());

    GLES20.glEnableVertexAttribArray(vertexHandle);
    GLES20.glEnableVertexAttribArray(normalHandle);
    GLES20.glEnableVertexAttribArray(textureCoordHandle);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures.get(2).mTextureID[0]);

    Matrix.translateM(modelViewMatrix, 0, (float)result[0], (float)result[1], (float)result[2]);
    Matrix.rotateM(modelViewMatrix, 0, 0, 1, 0, 0);
    Matrix.rotateM(modelViewMatrix, 0, 0, 0, 1, 0);
    Matrix.rotateM(modelViewMatrix, 0, 0, 0, 0, 1);
    Matrix.scaleM(modelViewMatrix, 0, 5, 5, 5);

    Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);

    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, modelViewProjection, 0);
    GLES20.glUniform1i(texSampler2DHandle, 0);
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.getInstance().getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT, sphere.getInstance().getIndices());
    GLES20.glDisable(GLES20.GL_BLEND);

    GLES20.glDisableVertexAttribArray(vertexHandle);
    GLES20.glDisableVertexAttribArray(normalHandle);
    GLES20.glDisableVertexAttribArray(textureCoordHandle);

The fillBuffer code is as follows:

protected Buffer fillBuffer(double[] array)
{
    // Convert to floats because OpenGL doesn't work on doubles, and manually
    // casting each input value would take too much time.
    // Each float takes 4 bytes
    ByteBuffer bb = ByteBuffer.allocateDirect(4 * array.length);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    for (double d : array)
        bb.putFloat((float) d);
    bb.rewind();

    return bb;        
}
1

There are 1 answers

1
ratchet freak On BEST ANSWER

The problem is when you add the points to the final arrays:

for(int c1 = 0; c1 < points.size(); c1 += 3)
{
    sphereVertices[c1] = points.get(c1)[0];
    sphereVertices[c1+1] = points.get(c1)[1];
    sphereVertices[c1+2] = points.get(c1)[2];
}

instead of using the same index for both the array and the list us separate ones:

for(int c1 = 0, i= 0; c1 < points.size(); c1++)
{
    sphereVertices[i++] = points.get(c1)[0];
    sphereVertices[i++] = points.get(c1)[1];
    sphereVertices[i++] = points.get(c1)[2];
}

same for the other arrays