En este video te explicamos en detalle todo el proceso para publicar una Aplicación Android…
Proyecto Android: construcción formas primitivas con OpenGL
3.
Proyecto Android: construcción formas primitivas con OpenGL
En este tutorial vamos a desarrollar un proyecto Android donde construiremos diversas formas primitivas 2D (cuadrado y triángulo) y 3D (cubo y pirámide), mediante el uso de la API OpenGL ES.
En la siguiente imagen puedes ver la lógica de esta Aplicación ejemplo:
Puedes descargar la imagen ampliada en el anterior tutorial
Veamos la estructura del proyecto:
En el siguiente ejemplo, se construyen diferentes formas geométricas, desde estructuras primitivas en 2D, hasta formas 3D en rotación. Para este proyecto hemos necesitado los siguientes elementos (al crear varias formas, sólo nos detendremos en el gráfico Cubo 3D):
Una clase principal llamada MainActivity, que heredará de la clase base Activity, donde se definen los elementos enlazados con sus recursos a nivel de layout, además desde esta Activity se realiza la llamada a una nueva View, encargada de construir los objetos gráficos. En esta clase se definen los siguientes componentes:
- Un componente RadioGroup formado por dos RadioButton para renderizar formas primitivas en 2D:
- Un componente RadioGroup formado por dos RadioButton para renderizar gráficos en 3D:
- Una clase llamada GLRender, que implementará la interfaz Renderer, encargada de construir la vista dónde se representará el objeto, a partir de los métodos que define onSurfaceCreated() , onDrawFrame() y OnSurfaceChanged().
- Una clase llamada Cubo3D dónde se definen los parámetros del objeto 3D que será invocado desde la clase GLRender:
- Número de caras.
- Número de vértices.
- Colores de las diferentes caras.
FormasPrimitivas/src/com.academiaandroid.formasprimitivas/MainActivity.java
Clase principal, que heredará de la clase base Activity, encargada de implementar la lógica de selección de la figura a representar, invocando a la vista que será construida desde la clase GLRender:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
[...] //Se declara la clase encargada de proporcionar la vista o view dónde se construirán los gráficos. private GLSurfaceView view; //Se definen los elementos necesarios para la selección de la forma a pintar. private RadioGroup radioGroupFormas, radioGroupFormas3D; private RadioButton radioCuadrado, radioTriangulo,radioCubo3D, radioPiramide3D; //Variable de tipo int para controlar la forma seleccionada. int flag; [...] [...] //Enlazamos los elementos con sus recursos a nivel de layout. radioGroupFormas = (RadioGroup)findViewById(R.id.radioGroupFormas); radioCuadrado = (RadioButton)findViewById(R.id.radioCuadrado); radioTriangulo = (RadioButton)findViewById(R.id.radioTriangulo); radioGroupFormas3D = (RadioGroup)findViewById(R.id.radioGroupFormas3D); radioCubo3D = (RadioButton)findViewById(R.id.radioCubo3D); radioPiramide3D = (RadioButton)findViewById(R.id.radioPiramide3D); //Se especifica que ningún elemento esté preseleccionado. radioGroupFormas.clearCheck(); //Se define el evento encargado de cazar el cambio de estado del componente RadioGroup, controlando que RadioButton ha sido seleccionado. radioGroupFormas.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if(radioCuadrado.isChecked()) { flag = 0; }else if(radioTriangulo.isChecked()) { flag = 1; } //Se instancia la clase GLSurfaceView, recibiendo el contexto de la aplicación como parámetro. view = new GLSurfaceView(getApplicationContext()); //El objeto GLSurfaceView invoca al método setRenderer, encargado de iniciar la construcción del objeto gráfico. view.setRenderer(new GLRender(flag)); setContentView(view); } }); [...] |
FormasPrimitivas/src/com.academiaandroid.formasprimitivas/GLRender.java
Crearemos una nueva clase llamada GLRender, que implementará la interfaz Renderer. Al implementar dicha interfaz, deberán sobrescribirse tres métodos encargados de definir la vista donde se mostrará el objeto gráfico:
1 2 3 4 5 6 7 |
//Clase que implementa la interfaz Renderer, encargada de //realizar las diferentes llamadas para dibujar la imagen. public class GLRender implements Renderer{ //Se declaran e inicializan dos variables de tipo float para establecer los valores del ángulo y velocidad de giro del elemento representado. private static float angulo = 90; private static float velocidadGiro = -1.5f; |
Dentro del constructor se crean las diferentes instancias de las formas a representar:
1 2 3 4 5 6 7 8 9 10 |
//Constructor donde se inicializan las clases de los diferentes elementos a construir. //Recibe como parámetro el elemento seleccionado en la Activity principal. public GLRender(int flag) { this.cuadrado = new Cuadrado(); this.cubo3D = new Cubo3D(); this.triangulo = new Triangulo(); this.piramide3D = new Piramide3D(); this.flag = flag; } |
Por último se sobrescriben los métodos implementados por la interfaz Renderer, encargados de la construcción y actualización de la vista:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//Método que será invocado si la geometría de la //vista cambia. @Override public void onSurfaceChanged(GL10 gl, int width, int height) { //Se comprueba que la altura no sea igual a 0. if(height == 0) { height = 1; } //Método que fija la vista recibiendo como parámetros la esquina inferior izquierda del rectángulo de visualización, por defecto (0,0), y los parámetros de ancho y alto. gl.glViewport(0, 0, width, height); //Se indica que se trabajará con la matriz PROJECTION. gl.glMatrixMode(GL10.GL_PROJECTION); //Se carga la identidad de la matriz. gl.glLoadIdentity(); //Establece la matriz de proyección en perspectiva. GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); } //Método que permite construir los diferentes objetos gráficos dentro de la vista definida. public void seleccionForma(int flag, GL10 gl) { //Limpia el buffer para preestablecer los valores de profundidad y color. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glLoadIdentity(); //Permite establecer el movimiento de translación indicando las coordenadas del eje X, Y y Z. gl.glTranslatef(0.0f, 0.0f, -5.0f); [...] if (flag == 2)//Elemento seleccionado en la Activity principal: Cubo 3D. { gl.glTranslatef(-1.0f, 0.0f, -5.0f); //Permite establecer el tamaño de la figura indicando las coordenadas del eje X, Y y Z. gl.glScalef(0.8f, 0.8f, 0.8f); //Permite establecer el grado de rotación, indicando además del ángulo con respecto a un punto, las coordenadas del eje X, Y y Z. gl.glRotatef(angulo, 1.0f, 1.0f, 1.0f); //Invocamos el método del objeto cubo3D encargado de establecer los parámetros del gráfico a dibujar. cubo3D.draw(gl); angulo += velocidadGiro; } |
Se crea una clase llamada cubo3D, dónde se establecen los diferentes parámetros de coordenadas, tanto de los vértices que representa la figura, como los colores asignados a cada una de sus caras, para finalmente implementar el método draw(GL10 gl), encargado de pintar la figura .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
[...] //Clase donde se establecen los parámetros para construir un cubo en 3 dimensiones. public class Cubo3D { //Se declara e inicializa una variable de tipo int, para posteriormente //recorrer el número de caras asignándole los colores definidos. int carasCubo = 6; //Declaramos un buffer de tipo float. private FloatBuffer fBuffer; //Array de tipo float donde se definen las coordenadas de cada uno de los //vértices que forman las diferentes caras del cubo. Un cubo se compone de //6 caras, y cada cara presenta 4 vértices con las coordenadas X, Y y Z. private float[] vertices = { //Coordenadas cara delantera -1.0f, -1.0f, 1.0f, // 0. izquierda-abajo-frontal 1.0f, -1.0f, 1.0f, // 1. derecha-abajo-frontal -1.0f, 1.0f, 1.0f, // 2. izquierda-arriba-frontal 1.0f, 1.0f, 1.0f, // 3. derecha-arriba-frontal [...] [...] //Método donde se definen los parámetros para pintar el cubo. public void draw(GL10 gl) { //Define los polígonos frontal y trasero enfrentados. gl.glFrontFace(GL10.GL_CCW); gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fBuffer); //Recorremos el número de caras, para asignarle el color a cada una de ellas. for (int cara = 0; cara < carasCubo; cara++) { gl.glColor4f(colores[cara][0], colores[cara][1], colores[cara][2], colores[cara][3]); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, cara*4, 4); } gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisable(GL10.GL_CULL_FACE); } [...] |
FormasPrimitivas/res/layout/activity_main.xml
Se definen dos componente de tipo RadioGroup, que estarán formados por dos componentes de tipo RadioButton cada uno, encargados de permitir al usuario seleccionar qué forma se desea mostrar en la vista:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<TableLayout android:layout_width="match_parent" android:layout_height="match_parent" > [...] <TableRow android:id="@+id/tableRow2" android:layout_width="wrap_content" android:layout_height="wrap_content" > <RadioGroup android:id="@+id/radioGroupFormas" android:layout_width="wrap_content" android:layout_height="wrap_content" > <RadioButton android:id="@+id/radioCuadrado" android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true" android:text="Cuadrado" /> <RadioButton android:id="@+id/radioTriangulo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Triángulo" /> </RadioGroup> </TableRow> [...] </TableLayout> |
Esta entrada tiene un comentario
Los comentarios están cerrados.
[…] En este tutorial vamos a desarrollar un proyecto Android, donde a partir de objetos 3D representados, se establecerán los parámetros de iluminación, además de implementar eventos de interacción con el usuario cómo la pantalla táctil o el acelerómetro. Para este ejemplo, sólo se hará hincapié en las modificaciones que hagamos respecto al primer caso práctico que publicamos sobre OpenGL. […]