martes, 30 de abril de 2013

OpenGL desde 0. Capitulo 2: Primitivas de pintado

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

Antes que nada quiero que tengáis en cuenta que gran parte de lo que os explicaré hoy no tiene utilidad más allá de los fines didácticos. Actualmente, con las nuevas versiones de OpenGL gran parte del tema de primitivas queda obsoleto, no obstante creo que es necesario que sepáis como funciona todo para ir aprendiendo el resto poco a poco (entendiendo las primitivas el resto viene directo).

En el capítulo anterior os comenté que existían unos bloques de código donde yo podía poner mi pintado de objetos 3D , los bloques glBegin / glEnd. La función glBegin acepta un parámetro que nos dicta de que manera va a utilizar los vértices que le pasemos a la tarjeta gráfica (si los va a entender como puntos sueltos, componentes de un triangulo o un cuadrado o bien extremos de una linea, por ejemplo)

Las primitivas gráficas que acepta OpenGL son las siguientes:

GL_POINTS: Cada vértice que le pasemos lo pintará como un punto. Esta primitiva puede ser útil en determinados momentos de desarrollo, ya lo explicaremos cuando lleguemos a ello.

GL_LINES: Acepta vértices de dos en dos siendo el primero el punto de inicio y el segundo el punto final de una linea.

GL_LINE_STRIP: Cada vértice que le pasemos será el punto final de una recta formada por este punto y el anterior y a la vez será el punto de inicio de una recta con el siguiente punto que le pasemos a OpenGL. Es decir, encadena puntos mediante rectas.

GL_LINE_LOOP: Es lo mismo que el GL_LINE_STRIP con la única diferencia que genera una recta adicional al final que une el punto incial de la lista con el punto final, formando siempre ciclos cerrados.

GL_TRIANGLES: Esta es una de las primitivas más usadas con diferencia. Acepta tres vértices que conformarán un triángulo. Si le llegan menos de tres vértices no pintará nada.

GL_TRIANGLE_STRIP: Esta directiva es la homóloga de GL_LINE_STRIP en el caso de los triángulos. Cada vértice nuevo genera un triángulo nuevo formado por este último punto y los dos anteriores. En su momento se usaba mucho para pintar modelos complejos (si no recuerdo mal los modelos MD2 del Quake2 se pintaban así).

GL_TRIANGLE_FAN: Este es un caso especial de pintado de triángulos ya que los triángulos nuevos se forman con el último punto que hemos introducido, el anterior y el primero de la lista. Al compartir todos los triángulos un punto (el inicial) acaban siempre quedando como un abanico. Su única utilidad era para simular circunferencias, pero hoy en dia está totalmente en desuso.

GL_QUADS: Esta primitiva está eliminada de las versiones más recientes de OpenGL. Esta primitiva utilizaba cuatro vértices para pintar un cuadrado. Su máxima utilidad residia en aplicaciones 2D como por ejemplo el PakEngine

GL_QUAD_STRIP: Esta primitiva está eliminada de las versiones más recientes de OpenGL. Su funcionamiento era homólogo a las otras primitivas STRIP pero en el caso de QUADS.

GL_POLYGON: Esta primitiva genera un polígono de tantos vértices como se le pase. En general tenía poco uso.


Para demostrar como funciona cada una de las primitivas he desarrollado un código muy sencillo que nos permitirá ver como renderiza cada primitiva teniendo todas  la misma información de vertices.

int DrawGLScene(GLvoid)
{
static int cont = 0 ;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glTranslatef(0.0f,0.0f,-6.0f);

cont++;

switch ( (cont/50) )
{
case 0: glBegin(GL_POINTS); break;
case 1: glBegin(GL_LINES); break;
case 2: glBegin(GL_LINE_STRIP); break;
case 3: glBegin(GL_LINE_LOOP); break;
case 4: glBegin(GL_TRIANGLES); break;
case 5: glBegin(GL_TRIANGLE_STRIP); break;
case 6: glBegin(GL_TRIANGLE_FAN); break;
case 7: glBegin(GL_QUADS); break;
case 8: glBegin(GL_QUAD_STRIP); break;
case 9: glBegin(GL_POLYGON); break;
default: cont = 0; glBegin(GL_POINTS); break;
}

glVertex3f(-1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();

return TRUE;
}

Aquí tenéis un vídeo que os demostrará como funciona: 


Espero que os haya gustado.... en breve colgaré otro tutorial para ir profundizando más sobre OpenGL.

Recordad que estoy abierto a sugerencias y dudas.

Nos vemos


LordPakusBlog

domingo, 28 de abril de 2013

OpenGL desde 0. Capitulo 1: Bases de OpenGL

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

En este blog ya hemos trabajado con OpenGL en varias ocasiones (por ejemplo en motores gráficos y pakengine), pero nunca os he explicado realmente como funciona OpenGL.

Las bases son sencillas:
- Todo lo que pintamos por pantalla es una representación ortogonal de objetos 3D. Es decir, "aplastamos" objetos 3D contra el monitor del PC.
- Estos objetos 3D estan representados por triangulos 2D que conforman su superficie. Es decir, el objeto 3D que vemos solo es una carcasa hecha con triangulos (en general) muy pequeños.
- Estos triangulos se definen mediante 3 puntos en coordenadas 3D. A la vez que definimos estos puntos normalmente también definimos coordenadas de cada punto a una región de una textura, haciendo que el modelo 3D que estamos representando tenga un "dibujo" que nos de mayor sensación de realidad (sin las texturas los objetos 3D serían de color sólido sin ningún tipo de detalle)

Resumiendo (a muy grosso modo) , openGL para pintar escenas 3D solamente usa puntos en 3D y relaciones de esos puntos con coordenadas 2D de una textura.

Fácil no?? Bueno, realmente esto solo es el principio. Solo hemos explicado como crear un mundo 3D, pero... como lo visualizamos?

Una vez el mundo 3D ya está creado hemos de generar una cámara que nos permita visualizar toda esa información. La cámara es la que se encargará de "aplastar" esos objetos 3D contra nuestra pantalla 2D. La gran pregunta es.... y como funciona todo ese proceso de "aplastamiento" ?? (A partir de ahora le llamaremos renderizado :) )
La respuesta es fácil de explicar pero dificil de entender al 100%. Con matemáticas, todo el proceso de renderizado se basa en cálculo matricial (tranquilos que todo el proceso lo hará la tarjeta gráfica) que si bien no deberemos saber como funciona realmente si que tendremos que irlo interiorizando con el tiempo. Esto quiere decir que openGL permite acumular matrices de transformación que nos permiten realizar diferentes tareas (traslaciones, rotaciones, etc...).

Ya sabemos la teoria de como crear un mundo 3D y de como visualizarlo, así que ahora solo nos falta un glosario rápido de funciones openGL para poder ir aprendiendo de códigos que vayamos viendo por la red (Nehe es un muy buen sitio para comenzar).


glViewport(0,0,width,height) : Esta función sirve para decirle a OpenGL en que zona de la ventana queremos que nos renderice la escena. Por defecto querremos que nos renderice la escena en toda la pantalla.


glMatrixMode (modo de matriz) : Esta función nos cambia el tipo de matriz sobre la que vamos a trabajar. Hay 4 tipos básicos de matrices sobre las que vamos a trabajar (ya las explicaremos más a fondo): GL_MODELVIEW, GL_PROJECTION,  GL_TEXTURE y  GL_COLOR.
    
glLoadIdentity(): Carga la matriz identidad. En matemáticas la matriz identidad es la mátriz neutra, es decir, limpiamos toda la pila de operaciones que teniamos hasta el momento.

glBegin/glEnd : Nos indica una zona de código donde le pasaremos a openGL los puntos de nuestros objetos 3D y el formato como queremos que los una (ya lo explicaremos en posteriores capitulos)

glVertex: Nos permite enviar puntos 3D que conforman un objeto 3D a la tarjeta gráfica. Ya iremos profundizando sobre esta funcionalidad.

Pese a que nos faltan todavía muchas funciones, con esto tendreis una pequeña ayuda para ir entendiendo códigos.

Espero que para ser el primer capítulo no se os haya hecho pesado y que os lo hayaís pasado bien. En el próximo capitulo iré introduciendo un poco de código para que lo veais más claro.

Nos vemos en el siguiente capitulo de openGL desde 0.


LordPakusBlog

viernes, 5 de abril de 2013

PakEngine. Capitulo 19. Interficie C++. Físicas dentro del modulo de sprites.

Artículo perteneciente a la sección del PakEngine

Hola a todos,

En el capitulo anterior introducimos el concepto de una clase nueva que englobaba la gestión de sprites a nivel gráfico.

Si nos remontamos al ejemplo del PakPong hay mucho código de juego que es de físicas (colisiones de pelota con las palas, con los extremos del campo, etc...) y que sería bastante bueno para el código introducirlo dentro del modulo de sprites a fin de tenerlo todo empaquetado en un mismo objeto.

Todo cuanto tenemos que hacer es introducir dos nuevas funciones dentro del modulo de sprite:


int Sprite::IsNotInside(Sprite *other)
{
return PHYSICS_NotInside(this->GetX(), this->GetY() , this->GetGraf(),   other->GetX(), other->GetY() , other->GetGraf() );
}

int Sprite::CollisionWith(Sprite *other)
{
return PHYSICS_Collision(this->GetX(), this->GetY() , this->GetGraf(),   other->GetX(), other->GetY() , other->GetGraf() );
}

Nos queda el código mucho más limpio y fácil de entender. Por ejemplo:

if ( bola->CollisionWith(player)  || bola->CollisionWith(foe) )

int mask = bola->IsNotInside(campo);


Aquí os dejo el código del PakPong. En próximos capítulos seguiremos trabajando para que el código del juego sea mínimo y esto nos permita mejorar nuestra calidad del código


void PlayerMovement(PakEngine *p, Sprite *player)
{
if(p->Input.KeyIsPressed("up") && ( player->GetY() > 10 ) )
player->Move(0,-1);

if(p->Input.KeyIsPressed("down") && (  player->GetY() < 410 ) )
player->Move(0,1);

if(p->Input.KeyIsPressed("left") && (  player->GetX() > 10 ) )
player->Move(-1,0);

if(p->Input.KeyIsPressed("right") && ( player->GetX() < 310 ) )
player->Move(1,0);
}

int main(void)
{
  PakEngine p("PakPong++",640,480);
  Sprite *bola = new Sprite(300,300,20,"Resources\\bola.png");
  Sprite *player = new Sprite(100,100,50,"Resources\\pala.png");
  Sprite *foe = new Sprite(400,200,50,"Resources\\pala.png");
  Sprite *campo  = new Sprite(0,0,100   ,"Resources\\campo.png");

  int xvel = 1, yvel = 1;
  int boing;

  boing = p.Sound.Load("Resources\\boing.wav");

 //Loop de engine
 while(p.Render())
 {
        PlayerMovement(&p,player);

bola->Move(xvel,yvel);

int mask = bola->IsNotInside(campo);

if(mask)
PAKENGINE_Play(boing);

if(mask&PHYSICS_RIGHT)
{
bola->Move(-1,0);
xvel = - (rand()%3 + 1);
yvel += (rand()%3 -1);
}

if(mask&PHYSICS_LEFT)
{
bola->Move(1,0);
xvel = (rand()%3 + 1);
yvel += (rand()%3 -1);
}

if(mask&PHYSICS_UP)
{
bola->Move(0,1);
xvel += (rand()%3 - 1);
yvel = (rand()%3 +1);
}

if(mask&PHYSICS_DOWN)
{
bola->Move(0,-1);
xvel += (rand()%3 - 1);
yvel = - (rand()%3 + 1);
}

if(bola->GetX() > foe->GetX())
{
if(foe->GetX() < 630)
foe->Move(1,0);
}
else
{
if(foe->GetX() > 320)
foe->Move(-1,0);
}

if(bola->GetY() > foe->GetY())
{
if( foe->GetY() < 410 )
foe->Move(0,1);
}
else
{
if ( foe->GetY() > 10 )
foe->Move(0,-1);
}

if ( bola->CollisionWith(player)  || bola->CollisionWith(foe) )
{
xvel =- xvel;
yvel += (rand()%3 -1);
}

p.Text.Draw(200,5,1,"PAK PONG");
campo->Draw();
player->Draw();
foe->Draw();
bola->Draw();
 }

 delete(&p);

 return 0;
}

Como podréis observar aún quedan bastantes cosas para ir arreglando pero poco a poco lo iremos mejorando.

Como siempre , podéis acceder al código completo desde el svn.

Espero que os haya gustado,

Nos vemos



LordPakusBlog

Entradas populares