"Ningún juego dura tanto hasta que te lo terminas como el que te programas tú mismo,
ninguno te absorbe tanto y ninguno te produce tanta satisfacción cuando lo has acabado"
Programacion de videojuegos
Sábado 24 de Junio del 2017

Menu principal
Colaborar (con PayPal)

Para continuar con el trabajo de esta Web y poder pagar el hosting, viene bien la ayuda que sea. Gracias a todos.

Importe: 

Ultimas descargas
19.Jan

Clase que permite dibujar texto en OpenGL con mucha facilidad.Usa FreeType2.Para ver que hace y c...


4. Primer dibujo Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   

En esta entrega del curso haremos el primer dibujo con OpenGL pero antes seguiremos aclarando conceptos, ya que con las ideas claras, despues todo sera mas facil.

El primer concepto sera el de los ejes de coordenadas que usaremos con OpenGL para que no haya confusion a la hora de escribir coordenadas.
Asi, en pantalla, sera el eje Y la altura, el eje X el ancho y el espacio virtual que se adentra en la pantalla sera el eje Z pero con coordenadas negativas.


Otro concepto importante es el orden de los puntos con que se dibuja una forma. Si se hace en el sentido de las agujas del reloj o en sentido contrario. Segun el orden, el motor grafico lo tomara como una cara delantera o una cara trasera de un objeto. Y nos preguntaremos ¿y que mas dara? pues no da igual ya que se puede decir al motor que dibuje las delateras solo (en objetos cerrados ahorraremos trabajo), solo las traseras o todas (esto se llama culling y se cambia con la funcion glCullFace(...) ). En el caso de OpenGL el orden sera en contra de las agujas del reloj.

Si no tenemos esto en cuenta podemos pensarnos las coordenadas de una maravillosa forma que queremos dibujar y va el motor grafico y no pinta nada ...

Otra cosa que me gustaria aclarar es la afirmacion que aparece en todos los manuales de OpenGL de que es una "maquina de estados".
Si ojeais manuales, en español o ingles, os encontrareis con semejante expresion que no es mas que una forma pomposa de indicar que las instrucciones de OpenGL se ponen de forma secuencial y las anteriores influyen en las siguientes.
Un ejemplo:
Indico al motor que voy a dibujar en color verde (por ejemplo), pongo la orden correspondiente en mi codigo ( glColor3f(0,1,0); )  y todo lo que dibuje despues sera verde.
Si quiero cambiar a rojo, pongo la orden de pintar en rojo ( glColor3f(1,0,0); ) y continuo pintando cosas, que seran rojas.

Sobre colores: uso un compuesto de tres colores para mezclarlos y hacer el que quiero.
El primer parametro es la componente rojo, el segundo el verde y el tercero azul.
Si uso numeros float el rango de cada componente sera de 0 a 1 (p.e. 0.4). Si uso tipo byte el rango sera de 0 a 255 (p.e. 130). Si mezclo rojo y verde da amarillo (p.e. 0.8,0.8,0.0 ). Todo ceros da negro y todo unos da blanco. Todos iguales dan algun tipo de gris, blanco o negro.
En fin, esta forma de indicar el color se llama RGB (Red Green Blue).

La nomenclatura de las ordenes de OpenGL.
Todas empiezan por gl y continuan por la orden con la primera en mayuscula, por ejemplo Vertex. Tras esto hay una serie de caracteres que pueden estar o no, segun la orden, y que significan el numero de coordenadas, el tipo, y una v en caso de que se pase a la funcion un puntero a los parametros.
Un ejemplo de nuevo:

1
glVertex3f(0.5,1.0,2.5);


Esta orden pinta un vertice de una figura. Se da el vertice con tres coordenadas de tipo float (GLfloat).

Otra opcion seria:

1
2
GLfloat vertices[] =  {0.5,1.0,2.5};
glVertex3fv(vertices);


Esta orden pinta el mismo vertice pero se pasan las coordenadas con un puntero a una matriz de tres float's.

OpenGL tiene tambien sus tipos de datos, disponibles a partir de incluir el fichero de encabezado:


Que se pueden usar como tipos normales en nuestro programa. No es necesario definir una variable que se va a pasar a una funcion OpenGL con uno de estos tipos, se puede definir una variable como float y pasarla a glVertex3f pero no esta mal acostumbrarse a usar los tipos especificos de OpenGL.

Para dibujar algo el metodo mas basico es el siguiente:
Se le dice a OpenGL que vamos a pintar don la funcion glBegin(...), se le pasan los vertices a dibujar con la funcion glVertex..., y cuando se termina de pasar vertices terminamos con glEnd(). Entre medias se pueden poner otras funciones como glColor...
Un ejemplo:

1
2
3
4
5
6
7
8
glBegin(GL_TRIANGLES); 
// Le digo a OpenGL el primer vertice de mi triangulo:
glVertex3f(0, 1, 0);
// El segundo vertice de mi triangulo:
glVertex3f(-1, 0, 0);
// El tercer vertice de mi triangulo:
glVertex3f(1, 0, 0);
glEnd();


Sobre este ejemplo dire primero que observaremos las coordenadas del eje Z siempre a 0, asi el triangulo se presenta frente a la pantalla (mas adelante veremos como colocar el punto de vista y la direccion a donde se mira, de forma que cambiara la relacion entre los ejes del mundo OpenGL y la pantalla).
La segunda apreciacion es sobre la " f " con que termina la funcion glVertex del ejemplo. Ya explique que corresponde al tipo de los parametros. Es lo mas habitual usar tipo float o sea numeros en coma flotante del tipo 35.675 o 2.5 o 0.1 o ... no hay que pensar solo en coordenadas exactas (la separacion sera un punto en vez de coma) .
La tercera cosa es sobre el parametro de la funcion glBegin(...).
Esta funcion acepta un parametro, en base al cual interpretara de que son los vertices y como debe pintarlos.
Asi pues si el parametro es GL_TRIANGLES como en el ejemplo, interpretara que cada punto es el vertice de un triangulo y pintara un triangulo cada tres vertices. Si el parametro fuera GL_POINTS pintaria un punto en pantalla por cada vertice, y si fuera GL_LINES pintaria una linea cada dos puntos.
El siguiente cuadro ilustra las posibilidades:



En este manual el uso mas normal sera GL_TRIANGLES, GL_QUADS y GL_LINES.


Vamos ahora con la parte del codigo.
Vamos a crear una funcion, dentro de la cual pintaremos nuestra escena (por sencilla que sea todavia, es ya una escena), se llamara Pinta() y se insertara dentro del bucle general del programa Windows:

1
2
3
4
5
6
7
8
9
10
11
12
while(TRUE) // Se ejecuta continuamente.
{
Pinta(); // Funcion que pinta algo y se repite continuamente al estar aqui.
 
if(PeekMessage(&Mensaje, NULL, 0, 0, PM_NOREMOVE)) // Exploramos los mensajes.
{// procesándolos adecuadamente
if(!GetMessage(&Mensaje, NULL, 0, 0)) return (int)Mensaje.wParam; // Fin
TranslateMessage(&Mensaje);
DispatchMessage(&Mensaje);
}
else WaitMessage(); // en caso contrario esperamos un mensaje
}


La funcion Pinta() sera nuestro bucle principal, que se repite continuamente. Aunque en pantalla muestre una imagen fija, en realidad se esta repintando constantemente. Es este caso eso no sirve para nada pero mas adelante, si introducimos movimiento (por algun algoritmo o por que presionemos una tecla) si le veremos la utilidad.
La funcion Pinta() la definiremos asi:

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
47
48
49
50
51
52
53
54
55
//--------------------------------------------------------------
// Funcion para pintar algo y que se repite continuamente. Se suele
// llamar bucle de juego o principal.
void Pinta()
{
// Borro el buffer de atras (BackBuffer), donde voy a pintar:
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
// Le digo a OpenGL que voy a pintar de este color, rojo:
glColor3f(1,0,0);
// Le digo a OpenGL que voy a pintar y con triangulos:
glBegin(GL_TRIANGLES);
// Le digo a OpenGL el primer vertice de mi triangulo:
glVertex3f(0, 0.5, 0);
// El segundo vertice de mi triangulo:
glVertex3f(-1, -0.5, 0);
// El tercer vertice de mi triangulo:
glVertex3f(1, -0.5, 0);
glEnd();
 
// Con esta fucion cambio el grosor de las lineas
// para que se vea mejor:
glLineWidth(5);
// Le digo a OpenGL que voy a pintar de este color, azul:
glColor3f(0,0,1);
// Le digo a OpenGL que voy a pintar y con lineas:
glBegin(GL_LINES);
// Linea del primer vertice al segundo:
glVertex3f(0, 0.5, 0);
glVertex3f(-1, -0.5, 0);
// Cambio de color para probar, ahora verde:
glColor3f(0,1,0);
// Linea del segundo vertice al tercero:
glVertex3f(-1, -0.5, 0);
glVertex3f(1, -0.5, 0);
// Cambio de color de nuevo, ahora una mezcla que da amarillo:
glColor3f(1,1,0);
// Linea del tercer vertice al primero de nuevo:
glVertex3f(1, -0.5, 0);
glVertex3f(0, 0.5, 0);
glEnd();
 
// Ahora voy a marcar el centro
// con un punto gordo para que se vea bien:
glPointSize(15);
// Y de color blanco:
glColor3f(1,1,1);
// Le digo a OpenGL que voy a pintar y con puntos:
glBegin(GL_POINTS);
glVertex3f(0, 0, 0);
glEnd();
 
// Cambio los bufferes de modo que presento lo que he dibujado en pantalla:
SwapBuffers(DevContex);
}


A pesar de tener comentarios autoexplicativos, el codigo merece alguna puntualizacion.
La primera funcion que se llama es glClear(...) para borrar los dos buferes mas importantes, el de color y el de profundidad. Esta operacion  se realiza contra el backbuffer en el caso del buffer de color, ya que al usar el sistema de doble buffer, siempre pintaremos en el de atras (backbuffer) y cuando este acabada nuestra escena se "suichean" los buferes de atras y adelante de forma que se presenta en pantalla lo que habiamos pintado atras y tenemos disponible para pintar el que antes era el de adelante y ahora ya no lo es (uffff que lio). Lo primero que hacemos es borrar el buffer, que tendra pintada la escena anterior, para pintar la nueva escena.
Si no lo hicieramos asi y pintaramos directamente en pantalla, se veria como se va pintando, por rapido que pinte nuestro ordenador.
El buffer de profundidad tambien se borra, ya que aunque en esta escena no seria necesario, cuando pintemos cosas en 3D si lo sera.
La untima funcion, SwapBuffers(...), es la que hace el cambio de entre el backbuffer y al frontbuffer, y por tanto es la que presenta de repente en pantalla todo lo que hemos dibujado en la funcion Pinta().
El motivo de que esta ultima funcion no empiece por gl es que la accion de cambiar los buferes es una funcion que debe implementarse para cada sistema operativo y por tando es una funcion propia de Windows. 

El resultado de nuestro programa deberia ser este:



Aunque os sugiero que empeceis a experimentar con formas, colores y coordenadas, y cambieis el programa.

Aqui esta el codigo del programilla al completo: usw4.cpp



¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
Andrés Pérez  - Error   |Registered |24-03-2013 19:30:36
Buenas, uso el C++ 2010 Express y hice todo lo que habia aqui, copie y pegue y
lei los comentarios para saber que hace cada linea... El error esta en que
cuando lo depuro solo sale la ventana en negro como la aplicacion anterior, es
decir que no sale el triangulo dibujado.
Vicengetorix   |87.221.167.xxx |24-03-2013 22:45:05
¿Haces la llamada a la función Pinta() desde el bucle plrincipal?
Marcos  - Error   |62.42.48.xxx |23-05-2012 22:20:19
1>MSVCRTD.lib(crtexe.obj) : error LNK2019: símbolo externo _main sin resolver
al que se hace referencia en la función ___tmainCRTStartup
me pasa esto que
hago¿?
Vicengetorix   |87.221.169.xxx |24-05-2012 21:56:58
En el capitulo 2 (primer programa), en los comentarios, ya hubo gente con este
problema. Te sugiero que les eches un vistazo.
En todo caso es de la
configuracion del proyecto.
Hay un proyecto de ejemplo en la zona descargable
que puedes usar o, por lo menos, ver para comparar con tus opciones.
JUAN JOSÉ  - Paso al siguiente capítulo.   |Registered |13-11-2011 22:46:54
Como es normal, el sistema de corta y pega no me estaba sirviendo para nada ya
que no me estaba enterando de nada. Modificar los colores y las coordenadas del
cuadrado (después del triángulo pasé al cuadrado) me sabía a poco. Al final
me doy por satisfecho aplicando colores aleatorios con la función rand. Está
bien porque me ha dado una idea de cuantas veces repinta el dibujo por segundo.
Además, he creado una variable para fijar los vértices del cuadrado de forma
que el cuadrado crece solo hasta que interrumpo (le sumo 0.001 cada vez que
repinta). Curioso que los colores solo varían y el cuadrado solo crece cuando
el cursor está sobre la ventana que hemos creado ¿por qué ocurre esto?.
Gracias por todo. Enhorabuena por la página es decir poco.
Vicengetorix   |85.53.196.xxx |13-11-2011 23:55:43
En el bucle general de windows, le indicamos que espere hasta que haya algun
evento. Con el raton fuera no hay eventos y no se ejecuta la funcion de pintar.

En siguientes capitulos cambiaremos eso.
Kokorico  - Waitmesage   |87.221.186.xxx |29-05-2010 01:05:27
Hola,
sobre todo muchisimas gracias por los tutoriales. Son geniales. Mi
preguntas es sobre el bucle while dentro del main. Por que acabas el procesado
de mensajes con un waitmessage? No seria mejor dejar que se ejecute otro ciclo y
que asi dibuje? Que sentido tiene esperar ali?
Vicengetorix   |194.179.126.xxx |29-05-2010 02:37:06
Nuestra escena, por ahora, no se mueve.
No tiene sentido dejar exausto el PC sin
un motivo.
En el capítulo 9 cambiaremos la parte del bucle y eliminaremos la
espera.
carlos   |189.202.64.xxx |25-01-2010 08:31:42
He buscado muchos tutoriales en internet y no encontraba muchos, estos me
parecen muy buenos pues abarcan de los temas mas sencillos a los mas complejos,
lo unico que le veo de malo es que usas visual c++, por lo que el codigo no es
portable, entonces lo que estoy haciendo para seguir tu tutorial es modificar un
codigo basico que tengo, difiere un poco de este codigo pero las funciones y
explicaciones de opengl me han sido utiles
Vicengetorix   |194.179.126.xxx |25-01-2010 14:50:23
Realmente el código que uso es casi totalmente portable a DEV-C (he estado
hacendo pruebas).
El único problema sería la librería corona, que habría que
compilarla para DEV-C o cambiarla por otra (lo que cambiaría algo el código, y
si alguien lo hace que me la mande). Sugiero SOIL pero puede ser cualquiera.
CarloCF  - Ta bien   |190.42.104.xxx |15-08-2009 07:05:58
Hola
Pues esl curso esta bueno. Tus comentarios son lo mejor ya que asi se
entiende mejor.

Carya por que no encontre este sitio alla por el 200 a.c. a
estas alturas estaria con mi propia compañia de Juegos y millonario.
Sigue a
delante

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 


Banner
Spanish Chinese (Simplified) English French German Japanese Portuguese Russian