Aprende a utilizar OpenGL ES en Aplicaciones Android

Aprende a utilizar OpenGL ES en Aplicaciones Android

Aprende a utilizar OpenGL ES en Aplicaciones Android: Casi todos los teléfonos Android disponibles en el mercado hoy en día cuenta con una unidad de procesamiento gráfico o GPU, para abreviar. Como su nombre indica, se trata de una unidad de hardware dedicado a la manipulación de los cálculos que por lo general están relacionadas con gráficos en 3D. Como desarrollador de aplicaciones, se puede hacer uso de la GPU para crear gráficos y animaciones complejas que se ejecutan a velocidades de cuadro muy altas.

En este momento hay dos API diferentes que puede utilizar para interactuar con la GPU de un dispositivo Android: Vulkan y OpenGL ES . Mientras Vulkan sólo está disponible en dispositivos con Android 7.0 o superior, OpenGL ES es compatible con todas las versiones de Android.

En este tutorial, te ayudaré a conocer el uso de OpenGL ES 2.0 en aplicaciones de Android.

Para poder seguir este tutorial, necesitará:

  • la última versión de Android Studio
  • un dispositivo Android compatible con OpenGL ES 2.0 o más alto
  • una versión reciente de Blender , o cualquier otro software de modelado 3D

OpenGL, que es la abreviatura para Open Graphics Library, es una API independiente de la plataforma que le permite crear gráficos 3D acelerados por hardware. OpenGL ES, abreviatura de OpenGL para sistemas integrados, es un subconjunto de la API.

OpenGL ES es un API de nivel muy bajo. En otras palabras, no ofrece métodos que le permiten crear rápidamente o manipular objetos en 3D. En su lugar, mientras se trabaja con él, que se espera para manejar manualmente tareas como crear los vértices individuales y las caras de objetos 3D, cálculo de varias transformaciones 3D, y la creación de diferentes tipos de shaders.

También vale la pena mencionar que el SDK y NDK de Android en conjunto le permiten escribir código relacionado con OpenGL ES en Java y C.

Debido a las API de OpenGL ES son una parte del marco de Android, que no tiene que agregar las dependencias a su proyecto para poder utilizarlos. En este tutorial, sin embargo, vamos a utilizar la biblioteca Apache Commons IO para leer el contenido de algunos archivos de texto. Por lo tanto, añadirla como una compiledependencia de su módulo de aplicación build.gradle archivo:

Además, con el fin de dejar de usuarios de Google Play que no tienen dispositivos compatibles con la versión de OpenGL ES que necesita de la instalación de su aplicación, agregue la siguiente <uses-feature>etiqueta al archivo de manifiesto de su proyecto:

El marco Android ofrece dos widgets que pueden actuar como un lienzo para sus gráficos en 3D: GLSurfaceViewy TextureView. La mayoría de los desarrolladores prefieren usar GLSurfaceView, y elegir TextureViewsólo cuando tienen la intención de superponer sus gráficos en 3D en otro Viewartilugio. Para la aplicación que vamos a crear en este tutorial, GLSurfaceViewserá suficiente.

Adición de un GLSurfaceViewwidget a tu archivo de diseño no es diferente de la adición de cualquier otro control.

Tenga en cuenta que hemos hecho el ancho de nuestro widget igual a su altura. Si lo hace, es importante porque el OpenGL ES sistema de coordenadas es un cuadrado. Si tiene que usar un lienzo rectangular, recuerde incluir su relación de aspecto, mientras que el cálculo de su matriz de proyección. Vas a aprender lo que es una matriz de proyección está en un paso posterior.

La inicialización de un GLSurfaceViewcontrol dentro de una Activityclase es tan simple como llamar al findViewById()método y pasando su identificador a ella.

Además, hay que llamar al setEGLContextClientVersion()método para especificar explícitamente la versión de OpenGL ES que va a utilizar para dibujar dentro del widget.

Aunque es posible crear objetos 3D en Java por parte de codificación de los ejes X, Y y Z de todos sus vértices, hacerlo es muy engorroso. El uso de herramientas de modelado 3D en cambio, es mucho más fácil. Blender es una de estas herramientas. Es de código abierto, potente y muy fácil de aprender.

El fuego de Blender y presiona X para borrar el cubo por defecto. A continuación, pulse Shift-A y seleccione malla> Torus . Ahora tenemos un objeto en 3D bastante complejo que consta de 576 vértices.

Torus en Blender

Para poder utilizar el toroide en nuestra aplicación para Android, debemos exportarlo como un archivo Wavefront OBJ. Por lo tanto, ir a Archivo> Exportar> Wavefront (.obj) . En la siguiente pantalla, dar un nombre al archivo OBJ, asegurarse de que el Triangulate Caras y Mantener Vertex Solicitar opciones están seleccionadas, y presione la exportación OBJ botón.

La exportación de objetos 3D como archivo Wavefront OBJ

Ya puede cerrar el Blender y mover el archivo OBJ a su proyecto de Android Studio activos carpeta.

Si usted no ha notado ya, el archivo OBJ hemos creado en el paso anterior es un archivo de texto, que se puede abrir con cualquier editor de texto.

OBJ archivo se abrió con un editor de texto

En el archivo, cada línea que comienza con una “V” representa un único vértice. Del mismo modo, cada línea de partida con un “f” representa una sola cara triangular. Mientras que cada línea de vértice contiene el X, Y, y Z de un vértice, cada línea cara contiene los índices de tres vértices, que juntas forman una cara. Eso es todo lo que necesita saber para analizar un archivo OBJ.

Antes de comenzar, crear una nueva clase Java llamada toro y añadir dos Listobjetos, uno de los vértices y uno de los rostros, ya que sus variables miembro.

La forma más fácil de leer todas las líneas individuales del archivo OBJ es utilizar la Scannerclase y su nextLine()método. Mientras bucle a través de las líneas y llenar las dos listas, se puede utilizar el Stringde la clase startsWith()método para comprobar si la línea actual se inicia con una “V” o una “f”.

No se puede pasar las listas de vértices y las caras de los métodos disponibles en la API de OpenGL ES directamente. Primero debe convertir en objetos de amortiguamiento. Para almacenar los datos de coordenadas del vértice, necesitaremos un FloatBufferobjeto. Para los datos de cara, que consiste simplemente en índices de vértice, un ShortBufferobjeto será suficiente.

De acuerdo con ello, agregue las siguientes variables miembro de la Torusclase:

Para inicializar las memorias intermedias, hay que crear primero un ByteBufferobjeto mediante el allocateDirect()método. Para el buffer vértices, asignar cuatro bytes para cada coordenada, lo que con las coordenadas son números de coma flotante. Una vez que la ByteBufferha creado objeto, puede convertirlo en un FloatBufferllamando a su asFloatBuffer()método.

Del mismo modo, crear otro ByteBufferobjeto para la memoria intermedia de caras. Esta vez, asignar dos bytes para cada índice de vértice porque los índices son unsigned shortliterales. Además, asegúrese de que utiliza el asShortBuffer()método para convertir el ByteBufferobjeto a una ShortBuffer.

Llenado de la memoria intermedia de vértices implica bucle a través de los contenidos de verticesList, la extracción de la X, Y y Z coordenadas de cada artículo, y llamar a la put()método para poner los datos dentro de la memoria intermedia. Debido a que verticesListcontiene sólo cadenas, hay que utilizar la parseFloat()de convertir las coordenadas de las cadenas de floatvalores.

Observamos que en el código anterior hemos utilizado el position()método para restablecer la posición de la memoria intermedia.

Llenado de la memoria intermedia de caras es ligeramente diferente. Debe utilizar el parseShort()método para convertir cada índice de vértice a un valor corto. Además, debido a que los índices comienzan desde un lugar de cero, hay que recordar que restar uno de ellos antes de ponerlos dentro de la memoria intermedia.

Para poder prestar nuestro objeto 3D, debemos crear un sombreado de vértices y un shader fragmento de la misma. Por ahora, se puede pensar en un sombreado como muy simple programa escrito en un lenguaje C, llamado glsl o GLSL para abreviar.

Un vertex shader, como es de suponer, es responsable del manejo de los vértices de un objeto 3D. Un fragmento de sombreado, también llamado un sombreado de píxeles, es responsable de la coloración de píxeles del objeto 3D.

Crear un nuevo archivo llamado vertex_shader.txt dentro de su proyecto res / prima carpeta.

Un vertex shader debe tener una attributevariable global en su interior con el fin de recibir datos de posición de vértice a partir del código de Java. Además, añadir una uniformvariable global para recibir una matriz vista-proyección desde el código Java.

Dentro de la main()función del vertex shader, debe establecer el valor de gl_positionuna variable incorporada GLSL que decide la posición final del vértice. Por ahora, sólo tiene que establecer su valor al producto de las uniformy attributevariables globales.

De acuerdo con ello, agregue el código siguiente al archivo:

Crear un nuevo archivo llamado fragment_shader.txt dentro de su proyecto res / prima carpeta.

Para mantener este breve tutorial, que ahora va a crear un fragment shader muy minimalista que simplemente asigna el color naranja a todos los píxeles. Para asignar un color a un píxel, dentro de la main()función de un fragmento de sombreado, puede utilizar la gl_FragColorvariable de built-in.

En el código anterior, la primera línea que especifica la precisión de los números de punto flotante es importante porque un fragment shader no tiene ninguna precisión predeterminada para ellos.

De vuelta en la Torusclase, ahora debe agregar código para compilar los dos shaders que ha creado. Antes de hacerlo, sin embargo, debe convertirlos a partir de recursos primas a las cadenas. La IOUtilsclase, que es una parte de la biblioteca Apache Commons IO, tiene un toString()método para hacer precisamente eso. El código siguiente muestra cómo se usa:

Código de los shaders’ hay que añadir a los objetos de sombreado de OpenGL ES. Para crear un nuevo objeto de sombreado, utilizar el glCreateShader()método de la GLES20clase. Dependiendo del tipo de objeto de sombreado que desea crear, puede pasar GL_VERTEX_SHADERo GL_FRAGMENT_SHADERa ella. El método devuelve un entero que sirve como una referencia al objeto de shader. Un objeto de shader de nueva creación no contiene ningún código. Para añadir el código de sombreado al objeto de sombreado, se debe utilizar el glShaderSource()método.

El siguiente código crea objetos de sombreado, tanto para el shader de vértice y el shader fragmento:

Ahora podemos pasar los objetos de sombreado al glCompileShader()método para compilar el código que contienen.

Mientras que la prestación de un objeto 3D, no se utiliza directamente los shaders. En su lugar, adjuntarlos a un programa y utilizar el programa. Por lo tanto, agregar una variable miembro a la Torusclase para almacenar una referencia a un programa de OpenGL ES.

Para crear un nuevo programa, utilice el glCreateProgram()método. Para unir los objetos de vértices y de sombreado fragmento a ella, utilizar el glAttachShader()método.

En este punto, puede vincular el programa y empezar a usarlo. Para ello, utilice el glLinkProgram()y glUseProgram()métodos.

Con los shaders y tampones preparados, tenemos todo lo que necesitamos para sacar nuestras toro. Añadir un nuevo método a la Torusclase llamada sorteo :

En un paso anterior, dentro del vertex shader, definimos una positionvariable para recibir datos de posición de vértice de código Java. Ahora es el momento de enviar los datos de posición de vértice a la misma. Para ello, primero debemos obtener un identificador para la positionvariable en nuestro código Java utilizando el glGetAttribLocation()método. Además, el mango debe estar habilitado mediante el glEnableVertexAttribArray()método.

De acuerdo con ello, agregue el código siguiente en el draw()método:

Para señalar el positionmango a nuestro buffer vértices, debemos usar el glVertexAttribPointer()método. Además de los vértices búfer en sí, el método prevé que el número de coordenadas por vértice, del tipo de las coordenadas, y el desplazamiento para cada vértice byte. Debido a que tenemos tres coordenadas por vértice y cada coordenada es una float, el desplazamiento de bytes debe ser 3 * 4.

Nuestra shader vértice también espera una matriz vista-proyección. Aunque tal una matriz no siempre es necesario, utilizando uno le permite tener un mejor control sobre cómo se procesa el objeto 3D.

Una matriz vista-proyección es simplemente el producto de la vista y de proyección matrices. Una matriz vista le permite especificar la ubicación de la cámara y el punto en que está mirando. Una matriz de proyección, por el contrario, le permite no sólo al mapa del sistema de OpenGL ES coordinar la perpendicular a la pantalla rectangular de un dispositivo Android, sino también especificar los planos cercanos y lejanos del tronco de cono de visión .

Para crear las matrices, puede simplemente crear tres floatmatrices de tamaño 16:

Para inicializar la matriz de proyección, se puede utilizar el frustumM()método de la Matrixclase. Se espera que la ubicación de la izquierda, aviones derecha, abajo, arriba, cerca y lejos de clip. Debido a que nuestro lienzo es un cuadrado ya, puede utilizar los valores -1y 1para la tapa planos de corte de izquierda y derecha, y la parte inferior y. Para los planos de corte de cerca y de lejos, no dude en experimentar con diferentes valores.

Para inicializar la matriz de vista, utilizar el setLookAtM()método. Se espera que las posiciones de la cámara y el punto en que está mirando. Usted está de nuevo libre de experimentar con diferentes valores.

Finalmente, para calcular la matriz de producto, utilice el multiplyMM()método.

Para pasar la matriz del producto a la vertex shader, debe obtener un identificador para su matrixvariable utilizando el glGetUniformLocation()método. Una vez que tenga el mango, puede apuntar a la matriz del producto mediante el glUniformMatrix()método.

Usted debe haber notado que todavía no hemos utilizado la memoria intermedia caras. Eso significa que todavía no hemos dicho OpenGL ES cómo conectar los vértices para formar triángulos, que servirán como caras de nuestro objeto 3D.

El glDrawElements()método permite el uso de las caras de amortiguamiento para crear triángulos. Como sus argumentos, se espera que el número total de los índices de vértice, el tipo de cada índice, y el tampón de caras.

Por último, no olvide desactivar el attributecontrolador ha activado antes de pasar los datos de vértice a la vertex shader.

Nuestra GLSurfaceViewReproductor necesita un GLSurfaceView.Rendererobjeto para ser capaz de representar gráficos 3D. Se puede utilizar el setRenderer()asociar un procesador con él.

Dentro del onSurfaceCreated()método de la renderizador, se debe especificar con qué frecuencia debe hacerse la gráfica 3D. Por ahora, vamos a procesar sólo cuando los cambios de gráficos en 3D. Para ello, pase el RENDERMODE_WHEN_DIRTYconstante al setRenderMode()método. Además, inicializar una nueva instancia de la Torusobjeto.

Dentro del onSurfaceChanged()método de la renderizador, se puede definir la anchura y la altura de la ventana gráfica utilizando el glViewport()método.

Dentro del onDrawFrame()método de la renderizador, añadir una llamada al draw()método de la Torusclase para dibujar realmente el toro.

En este punto, puede ejecutar su aplicación para ver el toro de color naranja.

Aplicación mostrando toro

Ahora ya sabe cómo utilizar OpenGL ES en aplicaciones de Android. En este tutorial, también ha aprendido cómo analizar un archivo Wavefront OBJ y extraer datos de vértices y de la cara de ella. Sugiero a generar un poco más de los objetos 3D usando Blender y tratar de hacerlas en la aplicación.

Aunque nos centramos sólo en OpenGL ES 2.0, entendemos que OpenGL ES 3.x es compatible hacia atrás con OpenGL ES 2.0. Eso significa que si usted prefiere el uso de OpenGL ES 3.x en su aplicación, puede simplemente reemplazar la GLES20clase con la GLES30o GLES31las clases.

Registrese en Channeldir.com Para estar más actualizado de noticias de tecnología


Gracias por leer este artículo, si le gusto por favor Like y Compártalo


Regístrese a nuestra gran comunidad tecnológica con: