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:
acien101@debian:~$ EXIT