Sprites en OpenGL

Vamos a empezar con un sencillo proyecto que puede ser la base de muchas pruebas, vamos a trabajar con OpenGL para dibujar sprites en pantalla. A partir de aquí se puede hacer cualquier cosa que se nos ocurra así que vamos a ver todo lo que intentaremos hacer de la forma mas clara … bueno … de la forma mas clara siempre que tengamos algunos conceptos de OpenGL claros.

Crear un proyecto en OpenGL

Este será el paso mas fácil de todos, abrimos el Xcode y seleccionamos File > New Proyect indicamos que queremos una «OpenGL ES Application» pulsamos el botón ‘choose’ e indicamos un nombre para el proyecto, en nuestro caso SpriteGL.

Esto ya nos creará un proyecto por defecto con un Quad dando vueltas (es curioso pero en OpenGL ES no existe los Quads … así que espero que sepáis lo que es un Triangle_Strip o un Triangle_Fan). Bueno … disfrutar de vuestro proyecto mientras podais porque el siguiente paso será destrozarlo.

Nuestra Clase de ayuda GL

El siguiente paso será crear una nueva clase donde vamos a meter de forma statica todos nuestros métodos de dibujado, en realidad sólo vamos a necesitar 3 métodos, pero será sutil tenerlo en una clase lista para usar en distintos proyectos, asi que vamos a por ella:

Vamos a File > New File y seleccionamos que queremos un NSObject subclass (este es tipo base del que todo hereda en Objective-C) Pulsamos Next, y le damos nombre a la clase.

Fijaros que en Location estamos creando la clase dentro del directorio /Classes/ no supone ningún problema crearlo en otro sitio pero hay que ser un poco ordenados. De todos modos si os equivocáis y lo creais en otra ubicación siempre podéis arrastrarlo y cambiarlo de directorio.

Total … seguimos y el Xcode nos creará los archivos GL.h y GL.m. Ahora es cuando empezaremos por fin a meter código, de momento un poco de Copy&Paste que siempre viene bien:

Vamos a GL.h y añadimos estas lineas

Como veis simplemente son un montonazo de imports .. que ahora mismo ni recuerdo para que son necesarios todos los de OpenGL pero que tampoco hacen mal a nadie por estar ahí, así que los dejamos. El QuartzCore es necesario para la carga de imágenes, luego explicaré como agregar el Framework necesario para que funcione.

Vamos a GL.m y añadimos

En primer lugar después de la implementación metemos esta variable global que vamos a usar luego, será para guardar las coordenadas del sprite que queremos dibujar:

y seguimos por implementar los 3 métodos

 GL.m (3.5 Kb)
 GL.h (541 Bytes)

Una breve explicación de nuestros metodos

loadTexture

Pues con ese nombre tan descriptivo esta función sirve para cargar una textura, ala, ahí queda eso.

setSprite

Este método se encarga de activar la textura que hemos cargado previamente con loadTexture, para este ejemplo nuestra textura está dividida en 4 sprites, así que añadimos un segundo parámetro ‘frame’ para indicarle que sprite queremos dibujar:

Este método dejará cargado en ‘spriteTexcoords’ las coordenadas necesarias para que todos los sprites que se envíen a pantalla lo hagan con ese frame que hemos marcado.

drawSprite

Y finalmente mediante este método dibujamos un sprite, solo tiene 4 parámetros que corresponde que la esquina superior izquierda y la esquina inferior izquierda … la típica rutina para dibujar sprites cuadrados.

Sólo debemos tener en cuenta una cosa … y es que yo, por llevar la contraria a todos, siempre ajusto la vista para que el centro de pantalla corresponda con las coordenadas <0,0> (en lugar de poner el inicio en la esquina superior como hacen los chicos buenos), esto quiere decir para dibujar un sprite en el centro de pantalla de 200 pixels tendríamos que poner:

Añadir CoreGraphics.Framework

Para que la carga de texturas funcione es necesario añadir este framework al proyecto, para ello vamos a la sección Frameworks de nuestro proyecto, pulsamos con el botón secundario del ratón y seleccionamos Add > Existing Frameworks.

Esto nos abre un navegador para seleccionar el Framework pero es un caos porque por defecto se abre en el directorio que le da la gana y encontrar el framework no será fácil. Podéis usar el buscador y poner el nombre del framework que queréis añadir pero en mi caso concreto me salen 8 distintos .. y hay que ir marcándolos uno a uno para ver que ruta tienen y si es el que necesitamos.

Total … para no perdernos en algo tan fácil como podía haber sido un par de clicks os recomiendo que lo busquéis por su ruta completa: Developer/ platforms/ iPhoneOs.platform/ Developer/ SDKs/ iPhoneOS2.2sdk/ System/ Library/ FrameWorks/ CoreGraphics.framework repufffffff

Vamos a dibujar

Ya tenemos nuestra clase GL que es capaz de cargar texturas y dibujar sprites, así que vamos a modificar ya el cuadradito que daba vueltas al principio para dejar nuestro código.

Empezaremos por tener algo que dibujar

Parece obvio pero no intentéis cargar ninguna imagen que no tengáis dentro de los recursos de vuestro proyecto. Empezamos por descargar esta imagen 4Sprites.png y dejarla dentro de la sección resources del proyecto:

Debereis marcar la casilla copy items si queréis que la imagen se guarde dentro del directorio del proyecto, esto sería lo normal:

Abrimos EAGLView.h

En este archivo podemos ver que tenemos una clase llamada EAGLView esta clase es la que tiene los buffers de dibujado, el Timer que controla el dibujado de pantalla, y los métodos necesarios para dibujar. Nosotros de momento solo vamos a añadir una variable, para guardar nuestra textura, y unos métodos nuevos para organizar mejor nuestro código:

Dentro de la interfaz añadimos esta linea:

Y debajo de los 3 métodos que tiene definidos (startAnimation, stopAnimation y drawView) vamos a añadir estos 4 métodos nuestros:

Abrimos EAGLView.h

Como hemos dicho esta clase es la que inicializa los buffers de dibujado y la que crea un Timer para dibujar la pantalla. Así que Lo primero que haremos será buscar el método layoutSubviews y lo modificamos para que quede así, únicamente es añadir las lineas 5 y 6:

Este método se llama cuando se inicializa la aplicación y se crea la Vista, lo que hacia antes era crear el buffer de dibujado y llamaba a drawView por primera vez, el propio método drawView era quien ponía la cámara del OpenGL, ahora hemos separado ese método para no repetir el proceso de la cámara.

Implementando metodos

Aquí van los 4 métodos que vamos a implementar:

Con todo esto ya podemos ir al método ‘drawView’ y, literalmente, borrar todo lo que tenga porque no sirve de nada, ya no. Ahora nuestro método drawView se va a comportar de la siguiente manera:

peeeeeeeero, si os atrevéis con un draw algo mas completo, podes probar esto:

 EAGLView.m (6.04 Kb)
 EAGLView.h (1.24 Kb)

Si todo va bien, os debería salir algo como esto .. los bichitos dando vueltas la mar de entretenidos ellos

Si tenéis algún problema con los archivos aquí os dejo el proyecto entero
 GL-Sprite.zip (180.51 Kb)

Y aquí os dejo la actualización navideña con el proyecto preparado para funcionar con la version 3.1.2, en esta versión nos han modificado el template para las aplicaciones OpenGl, así que echar un vistazo al proyecto, el código no cambia nada … solo la organización de clases.
 SpriteGL.zip for 3.1.2 (730.16 Kb)

You may also like...

10 Responses

  1. n3wrotyk dice:

    Se agradece este tipo de tutoriales, la verdad es que la info sobre iphone es abrumadora y tener un programilla para poder encarar el desarrollo siempre viene bien :-p.

  2. atdcr dice:

    Justo estaba buscando este tipo de tutorial pero tengo un problema y es que las texturas no las carga, solo me sale en pantalla 4 cuadros blancos sin las imagenes, no podrias poner un zip con tu solucion para ver que hice mal. Gracias.

    • neofar dice:

      Bueno … algo es algo, si no te aparecen las imágenes solo puede ser por 2 razones …. o no se ha cargado la textura, o no se ha activado al enviar un sprite.

      Supongo que la primera causa la tendrás mas que comprobada … si la textura se ha cargado correctamente textureId debería valer 1 (ya que es el indice de la textura dentro del entorno GL) si vale cualquier otra cosa es que no ha podido cargar la textura.

      Y por otro lado asegurate que no te has comido las lineas que activan la textura:

      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, texture);

      La primera linea activa las texturas, le dice al OpenGL que vamos a usar una textura (en la siguiente linea le decimos que textura vamos a usar)
      … de todos modos … venga vale, te subiré el ejemplo

  3. Nitz dice:

    Buenas, muchas gracias por el tuto, era justo lo que buscaba.

    Pero me ocurre lo mismo que a @atdcr, me aparece un cuadro en blanco (usando la primera implementación de drawView).

    El código es clavado al que tienes (copy paste vamos). Me salen dos warnings, diciéndome que no estoy pasando un tipo de variable correcto al hacer:

    spriteImage = [UIImage imageNamed:cadena].CGImage;

    y

    textureid = [GL loadTexture:@»4Sprites.png»];

    ¿Alguna idea? ¿Puedes subir el zip please?

    Saludos 😛

  4. Nitz dice:

    Vale, creo que ya he encontrado el error.

    Lo que pasa es que no habilitas el GL_BLEND en tu método «setSprite».

    Aquí traigo un apaño:
    + (void)setSprite:(GLuint)texture frame:(int)frame
    {
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_TEXTURE_2D);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glBindTexture(GL_TEXTURE_2D, texture);

    GLfloat x1 = (frame % 2) / 2.0f;
    GLfloat y1 = (frame / 2) / 2.0f;
    GLfloat x2 = x1 + 0.5f;
    GLfloat y2 = y1 + 0.5f;

    // preparamos el array con las coordenadas de la textyra
    spriteTexcoords[0] = x1; spriteTexcoords[1] = y1;
    spriteTexcoords[2] = x2; spriteTexcoords[3] = y1;
    spriteTexcoords[4] = x2; spriteTexcoords[5] = y2;
    spriteTexcoords[6] = x1; spriteTexcoords[7] = y2;
    }

    Saludos y gracias por el tuto 😉

  5. neofar dice:

    Cierto, pero no es necesario meterlo en el setSprite, estos 3 parámetros son generales y basta con activarlos una vez, sería mejor añadirlos en nuestro metodo ‘inicializar’

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    Luego ya si interesa el blend lo puedes activar o no, pero la blendFuncion es raro que se tenga que cambiar sobre la marcha

  6. Nitz dice:

    Mmm… ¿Seguro? Estoy probando en hacer lo que dices: dejar el setSprite intacto y setear el blend en el init.

    Lamentablemente, me aparece la imagen en blanco.

    ¿Lo habías probado?

  7. neofar dice:

    Pues no, lo del blend no lo habia probado pero son funciones de configuracion del motor, no es necesario cambiarlas ni establecerlas al dibujar cada sprite si no es por implementar algun efecto de transparencia.

    De todos modos he modificado el tutorial y os he subido los fuentes, mira las lineas nuevas que han aparecido en la función ‘inicializar’ puedes hacer experimentos ahí comentando lineas.

    las glTexParameter realmente no afectarán que el sprite se vea o no, son solo configuraciones de los filtros que se aplican al ampliar o reducir las texturas.

  8. Néstor dice:

    pues a mi me saca 5 errores, cada uno en EAGLView.h:

    error: expected specifier-qualifier-list before ‘GLuint’
    error: expected specifier-qualifier-list before ‘GLuint’
    error: expected specifier-qualifier-list before ‘GLuint’
    error: expected specifier-qualifier-list before ‘GLint’
    error: expected specifier-qualifier-list before ‘GLint’

  9. neofar dice:

    Este proyecto tiene más de un año, si lo intentas hacer ahora tienes que tener en cuenta que el template que trae el Xcode ha cambiado bastante (para dar soporte a la version de OpenGlEs que incorpora el nuevo 3GS).

    De todos modos el error que te sale tampoco es cosa de otro mundo .. probablemente falta algún import con las librerías OGL.

    He agregado al post un .zip con el ejemplo funcionando en la ultima versión del Xcode, lo unico que tienes que tener en cuenta ahora es que toda la logica se hace en el ES1Renderer en lugar del EAGLView

    Si tienes dudas pregunta, un saludo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *