Boxes
Hoy toca un poco de 3D, vamos a dibujar unas cuantas cajitas, no deberia suponer ninguna dificultad, para esto nos hará faclta activar el cull_face y el depth_test (que si me ha supuesto algun quebradero de cabeza que aun no comprendo porque).

Proyecto + FrameWork
Como siempre empezamos por crear un proyecto vacio, agregamos el framework CoreGraphics como siempre siguiendo nuestro tutorial de Sprites en OGL, que ademas ahora tiene los sources para descargar.
Box
A continuación vamos a crear una clase Box para dibujar cada caja … seguro que mi método os va a asustar horrores, pero tiene su lógica: no me interesa dibujar la caja como un objeto solido a base de una lista de vértices y una definición de caras. Yo quiero dibujar un plano cuadrado, y luego rotarlo para formar cada lado del cubo. Esto me permitirá en un futuro (espero no muy lejano) añadir métodos a esta clase para dibujar los lados con mas control
| C | | copy code | | ? |
| 01 | |
| 02 | @interface Box : NSObject { |
| 03 | GLfloat px,py,pz; |
| 04 | GLfloat spriteTexcoords[8]; // coordenadas para la textura |
| 05 | GLfloat spriteVertices[12]; // coordenadas para los vertices |
| 06 | GLfloat spriteNormals[12]; // normales |
| 07 | } |
| 08 | |
| 09 | - (void)init:(GLfloat)x y:(GLfloat)y z:(GLfloat)z; |
| 10 | - (void)Render; |
| 11 | - (void)drawPlane:(int)rx ry:(int)ry rz:(int)rz; |
| 12 |
Por ahora solo nos interesa ver el método Init para darle una posición a la caja y el Render.
| C | | copy code | | ? |
| 01 | |
| 02 | |
| 03 | @implementation Box |
| 04 | |
| 05 | - (void)init:(GLfloat)x y:(GLfloat)y z:(GLfloat)z |
| 06 | { |
| 07 | px = x; py = y; pz = z; |
| 08 | |
| 09 | // coordenadas |
| 10 | spriteVertices[0]=-1.0f; spriteVertices[1]= -1.0f;spriteVertices[2]= -1.0f; |
| 11 | spriteVertices[3]= 1.0f; spriteVertices[4]= -1.0f; spriteVertices[5]=-1.0f; |
| 12 | spriteVertices[6]=-1.0f; spriteVertices[7]= -1.0f; spriteVertices[8]= 1.0f; |
| 13 | spriteVertices[9]= 1.0f; spriteVertices[10]=-1.0f;spriteVertices[11]= 1.0f; |
| 14 | |
| 15 | // texturas |
| 16 | spriteTexcoords[0] = 0.0f; spriteTexcoords[1] = 0.0f; |
| 17 | spriteTexcoords[2] = 1.0f; spriteTexcoords[3] = 0.0f; |
| 18 | spriteTexcoords[4] = 0.0f; spriteTexcoords[5] = 1.0f; |
| 19 | spriteTexcoords[6] = 1.0f; spriteTexcoords[7] = 1.0f; |
| 20 | |
| 21 | // normales |
| 22 | spriteNormals[0] = 0.0f; spriteNormals[1] = 1.0f; spriteNormals[2] = 0.0; |
| 23 | spriteNormals[3] = 0.0f; spriteNormals[4] = 1.0f; spriteNormals[5] = 0.0; |
| 24 | spriteNormals[6] = 0.0f; spriteNormals[7] = 1.0f; spriteNormals[8] = 0.0; |
| 25 | spriteNormals[9] = 0.0f; spriteNormals[10] = 1.0f; spriteNormals[11] = 0.0; |
| 26 | |
| 27 | } |
| 28 | |
| 29 | - (void)Render |
| 30 | { |
| 31 | |
| 32 | glPushMatrix(); |
| 33 | glColor4f(1.0f, 1.0f, 1.0f, 1.0f); |
| 34 | |
| 35 | GLfloat sep = 2.50f; |
| 36 | glTranslatef(px * sep, py * sep, pz * sep); |
| 37 | |
| 38 | [self drawPlane:3 ry:0 rz:0]; |
| 39 | [self drawPlane:3 ry:1 rz:0]; |
| 40 | [self drawPlane:3 ry:2 rz:0]; |
| 41 | [self drawPlane:3 ry:3 rz:0]; |
| 42 | [self drawPlane:0 ry:0 rz:0]; |
| 43 | [self drawPlane:2 ry:0 rz:0]; |
| 44 | |
| 45 | glPopMatrix(); |
| 46 | } |
| 47 | |
| 48 | - (void)drawPlane:(int)rx ry:(int)ry rz:(int)rz |
| 49 | { |
| 50 | glPushMatrix(); |
| 51 | glRotatef(90 * rz, 0, 0, 1); |
| 52 | glRotatef(90 * ry, 0, 1, 0); |
| 53 | glRotatef(90 * rx, 1, 0, 0); |
| 54 | |
| 55 | glEnableClientState(GL_VERTEX_ARRAY); |
| 56 | glVertexPointer(3, GL_FLOAT, 0, spriteVertices); |
| 57 | |
| 58 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
| 59 | glTexCoordPointer(2, GL_FLOAT, 0, spriteTexcoords); |
| 60 | |
| 61 | glEnableClientState( GL_NORMAL_ARRAY ); |
| 62 | glNormalPointer ( GL_FLOAT, 0, spriteNormals ); |
| 63 | |
| 64 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 65 | |
| 66 | glPopMatrix(); |
| 67 | } |
| 68 | @end |
| 69 |
Nuestro método Init establece las coordenadas del único plano, un plano horizontal sobre el eje XZ y que está a una altura en y = -1, luego el método Render llama 6 veces a drawPlane dándole las rotaciones necesarias para poner ese plano en todas las caras del cubo…. una forma cómoda de trabajar aunque una completísima aberración para las optimizaciones, lógicamente para dibujar 4 cubos tampoco tendrá importancia.
Box.m (1.91 Kb)
Box.h (593 Bytes)
EAGLView
El siguiente paso será inicializar nuestro programa y dibujar algunos cubos en pantalla, asi que vamos a ir directamente a ello. Empezamos por añadir estas variables a nuestra clase: la textura del cubo y un array de 7 cubos:
| C | | copy code | | ? |
| 1 | |
| 2 | GLuint texture; |
| 3 | Box *box[7]; |
| 4 |
A continuación vamos a EAGLView.m y activamos este define, esto lo que hace es generar el buffer de profundidad, este buffer es el que se encarga de dibujar la profundidad de cada polígono que dibujamos en pantalla, de modo que el motor sabe en todo momento que polígonos solapan a otros. Es necesario usarlo siempre que trabajemos en 3D
| C | | copy code | | ? |
| 1 | |
| 2 | #define USE_DEPTH_BUFFER 1 |
| 3 |
Y ahora ya podemos ir a nuestro metodo Inicializar:
| C | | copy code | | ? |
| 01 | |
| 02 | |
| 03 | -(void)inicializar |
| 04 | { |
| 05 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 06 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 07 | glEnable(GL_TEXTURE_2D); |
| 08 | |
| 09 | // Activamos el Blend |
| 10 | glEnable(GL_BLEND); |
| 11 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| 12 | |
| 13 | // Activamos el Cull Face |
| 14 | glEnable(GL_CULL_FACE); |
| 15 | glFrontFace(GL_CW); |
| 16 | |
| 17 | // Activamos el DepthTest |
| 18 | glDepthFunc(GL_LESS); |
| 19 | glEnable(GL_DEPTH_TEST); |
| 20 | |
| 21 | // Ponemos una luz |
| 22 | GLfloat lightPosition1[] = { 10.0f, 10.0f, -10.0f, 1.0f }; |
| 23 | GLfloat lightDiffuse1[] = { 1.0f, 1.0f, 1.0f, 1.0f }; |
| 24 | GLfloat lightAmbient1[] = { 0.4f, 0.4f, 0.8f, 1.0f }; |
| 25 | glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient1); |
| 26 | glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse1); |
| 27 | glLightfv(GL_LIGHT0, GL_POSITION, lightPosition1); |
| 28 | glEnable(GL_LIGHT0 ); |
| 29 | |
| 30 | glEnable(GL_LIGHTING); |
| 31 | |
| 32 | // bucle para inicializar las 7 cajas |
| 33 | for(int i=0;i<7;i++) |
| 34 | box[i] = [Box alloc]; |
| 35 | |
| 36 | [box[0] init: 0 y: 0 z: 0]; // Centro |
| 37 | [box[1] init: 1 y: 0 z: 0]; // x = 1 |
| 38 | [box[2] init:-1 y: 0 z: 0]; // x = -1 |
| 39 | [box[3] init: 0 y: 1 z: 0]; // y = 1 |
| 40 | [box[4] init: 0 y:-1 z: 0]; // y = -1 |
| 41 | [box[5] init: 0 y: 0 z: 1]; // z = 1 |
| 42 | [box[6] init: 0 y: 0 z:-1]; // z = -1 |
| 43 | |
| 44 | // Cargamos la textura |
| 45 | texture = [GL loadTexture:"Box.png"]; |
| 46 | } |
| 47 |
Box.png (712 Bytes)
En nuestro método clearBuffer deberemos añadir el borrado del buffer de profundidad, por defecto y hasta ahora solo habíamos limpiado el buffer de dibujado:
| C | | copy code | | ? |
| 01 | |
| 02 | -(void)clearBuffer |
| 03 | { |
| 04 | [EAGLContext setCurrentContext:context]; |
| 05 | glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); |
| 06 | glClear(GL_COLOR_BUFFER_BIT); |
| 07 | glClear(GL_DEPTH_BUFFER_BIT); |
| 08 | } |
| 09 | <code> |
| 10 | |
| 11 | Y finalmente nuestro metodo Draw ... simplemente activa la textura y renderiza las 7 cajas que hemos creado |
| 12 | |
| 13 | <code lang="c"> |
| 14 | |
| 15 | // Updates the OpenGL view when the timer fires |
| 16 | - (void)drawView |
| 17 | { |
| 18 | static GLfloat anglex = 0.0f; anglex+=0.6f; |
| 19 | static GLfloat angley = 0.0f; angley+=0.5f; |
| 20 | static GLfloat anglez = 0.0f; anglez+=0.3f; |
| 21 | |
| 22 | [self clearBuffer]; |
| 23 | |
| 24 | glLoadIdentity(); |
| 25 | glTranslatef(0,0,-8); |
| 26 | |
| 27 | glRotatef(anglex, 1, 0, 0); |
| 28 | glRotatef(angley, 0, 1, 0); |
| 29 | glRotatef(anglez, 0, 0, 1); |
| 30 | |
| 31 | // Activamos la textura |
| 32 | glEnable(GL_TEXTURE_2D); |
| 33 | glBindTexture(GL_TEXTURE_2D, texture); |
| 34 | |
| 35 | [box[0] Render]; |
| 36 | [box[1] Render]; |
| 37 | [box[2] Render]; |
| 38 | [box[3] Render]; |
| 39 | [box[4] Render]; |
| 40 | [box[5] Render]; |
| 41 | [box[6] Render]; |
| 42 | |
| 43 | [self swapBuffer]; |
| 44 | } |
| 45 | |
| 46 |
EAGLView.m (7.3 Kb)
EAGLView.h (1.29 Kb)
4 Comments
Eskema on August 15th, 2009
Me surge una duda que tal vez estoy obviando. Si para dibujar un plano necesitamos 2 triangulos, lo cual son 6 vertices, ¿por que creas 12?, GLfloat spriteVertices[12];
¿No deberia ser solo 6? GLfloat spriteVertices[6];
Saludos,
neofar on August 21st, 2009
se necesitan 2 triángulos, estos 2 triángulos comparten 2 vértices por lo que en realidad necesitamos 4 vértices …. y cada vértice tiene 3 coordenadas .. así que en nuestro array debe tener un espacio suficiente para que quepan 12 coordenadas
Néstor on December 29th, 2009
no! esos codigos me generan monton de errores

Tony S. on April 22nd, 2009
Felicitaciones por tu blog. Es estupendo para ir aclarándose con esto del OpenGL.
Saludos