Esfera en OpenGl ES 2.0 Android

Hola!

Estos días estoy trabajando en un proyectillo para pasar un ejercicio de dibujo técnico a 3D. Y para hacer los puntos necesitaba esferas. El problema es que en OpenGL hacemos las figuras por medio de triangulos. Sí, una esfera por medio de triangulos. ¿Cómo?

Viendo lo que se me iba a costar pensé en más de una vez en hacer un cubo para salir del paso. Busqué si alguien lo había hecho y no encontraba ninguna información decente, solo encontraba de otros lenguajes. Hasta que me encontré con el repositorio de JimSeker que tenía hecha una esfera pero para otra versión. Por lo que tenía que actualizar a como se hace ahora.

Visto que la documentación de como hacer esferas en OpenGl Es 2.0 es nula, me veo obligado a compartir el código, el código completo de la aplicación en mi github. Espero que sea de ayuda :)

public class Sphere {

    private final String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +
                    "attribute vec4 vPosition;" +
                    "void main() {" +
                    // The matrix must be included as a modifier of gl_Position.
                    // Note that the uMVPMatrix factor *must be first* in order
                    // for the matrix multiplication product to be correct.
                    "  gl_Position = uMVPMatrix * vPosition;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    private FloatBuffer vertexBuffer;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;

    // Set color with red, green, blue and alpha (opacity) values
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    private final int mProgram;

    public Sphere(int stacks, int slices, float radius, float squash) {

        this.m_Stacks = stacks;
        this.m_Slices = slices;
        this.m_Radius = radius;
        this.m_Squash = squash;

        init(m_Stacks, m_Slices, radius, squash, "dummy");

        int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram();

        // add the vertex shader to program
        GLES20.glAttachShader(mProgram, vertexShader);

        // add the fragment shader to program
        GLES20.glAttachShader(mProgram, fragmentShader);

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(mProgram);
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    public void draw(float[] mvpMatrix) {

        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        // get handle to shape's transformation matrix
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, (m_Slices+1)*2*(m_Stacks-1)+2);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }


    float m_Scale;
    float m_Squash;
    float m_Radius;
    int m_Stacks, m_Slices;

    private void init(int stacks,int slices, float radius, float squash, String textureFile) {
        float[] vertexData;
        float[] colorData;
        float colorIncrement=0f;

        float blue=0f;
        float red=1.0f;
        int vIndex=0;				//vertex index
        int cIndex=0;				//color index

        m_Scale=radius;
        m_Squash=squash;

        colorIncrement=1.0f/(float)stacks;

        {
            m_Stacks = stacks;
            m_Slices = slices;

            //vertices

            vertexData = new float[ 3*((m_Slices*2+2) * m_Stacks)];

            //color data

            colorData = new float[ (4*(m_Slices*2+2) * m_Stacks)];

            int phiIdx, thetaIdx;

            //latitude

            for(phiIdx=0; phiIdx < m_Stacks; phiIdx++)
            {
                //starts at -90 degrees (-1.57 radians) goes up to +90 degrees (or +1.57 radians)

                //the first circle

                float phi0 = (float) Math.PI * ((float)(phiIdx+0) * (1.0f/(float)(m_Stacks)) - 0.5f);

                //the next, or second one.

                float phi1 = (float) Math.PI * ((float)(phiIdx+1) * (1.0f/(float)(m_Stacks)) - 0.5f);

                float cosPhi0 = (float) Math.cos(phi0);
                float sinPhi0 = (float) Math.sin(phi0);
                float cosPhi1 = (float) Math.cos(phi1);
                float sinPhi1 = (float) Math.sin(phi1);

                float cosTheta, sinTheta;

                //longitude

                for(thetaIdx=0; thetaIdx < m_Slices; thetaIdx++)
                {
                    //increment along the longitude circle each "slice"

                    float theta = (float) (-2.0f*(float) Math.PI * ((float)thetaIdx) * (1.0/(float)(m_Slices-1)));
                    cosTheta = (float) Math.cos(theta);
                    sinTheta = (float) Math.sin(theta);

                    //we're generating a vertical pair of points, such
                    //as the first point of stack 0 and the first point of stack 1
                    //above it. This is how TRIANGLE_STRIPS work,
                    //taking a set of 4 vertices and essentially drawing two triangles
                    //at a time. The first is v0-v1-v2 and the next is v2-v1-v3. Etc.

                    //get x-y-z for the first vertex of stack

                    vertexData[vIndex+0] = m_Scale*cosPhi0*cosTheta;
                    vertexData[vIndex+1] = m_Scale*(sinPhi0*m_Squash);
                    vertexData[vIndex+2] = m_Scale*(cosPhi0*sinTheta);

                    vertexData[vIndex+3] = m_Scale*cosPhi1*cosTheta;
                    vertexData[vIndex+4] = m_Scale*(sinPhi1*m_Squash);
                    vertexData[vIndex+5] = m_Scale*(cosPhi1*sinTheta);

                    colorData[cIndex+0] = (float)red;
                    colorData[cIndex+1] = (float)0f;
                    colorData[cIndex+2] = (float)blue;
                    colorData[cIndex+4] = (float)red;
                    colorData[cIndex+5] = (float)0f;
                    colorData[cIndex+6] = (float)blue;
                    colorData[cIndex+3] = (float)1.0;
                    colorData[cIndex+7] = (float)1.0;

                    cIndex+=2*4;
                    vIndex+=2*3;
                }

                blue+=colorIncrement;
                red-=colorIncrement;

                // create a degenerate triangle to connect stacks and maintain winding order

                vertexData[vIndex+0] = vertexData[vIndex+3] = vertexData[vIndex-3];
                vertexData[vIndex+1] = vertexData[vIndex+4] = vertexData[vIndex-2];
                vertexData[vIndex+2] = vertexData[vIndex+5] = vertexData[vIndex-1];
            }

        }

        makeFloatBuffer(vertexData);
    }

    public void  makeFloatBuffer(float[] arr)
    {
        ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(arr);
        vertexBuffer.position(0);

    }
}

El resultado final es:

Ejemplo sphera

acien101@debian:~$ EXIT

comments powered by Disqus