"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
Inicio Curso de programación de juegos 3. Empezamos con OpenGL
Domingo 22 de Octubre 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...


3. Empezamos con OpenGL Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   
oglLa libreria grafica que vamos a usar es OpenGL. Ahora comenzamos a acostumbrarnos a su uso y entender como funciona.
Ya explique porque usaremos OpenGL. Ahora una breve explicacion para aclarar algun tema que suele ser confuso cuando se empieza con esta libreria grafica.
Lo normal es pensar que hace falta bajarse unas librerias para programar con OpenGL y un "run time" para ejecutar los programas hechos con ella. Con DirectX si hace falta y ocupa lo suyo; pero con OpenGL no hay "run time" y solo hacen falta dos pequeñas librerias que vienen con los compiladores (en realidad solo haria falta una).
En realidad el nucleo de OpenGL instalado en tu ordenador, esta en el driver grafico de tu tarjera de video y es el fabricante de la misma quien la implementa para su hardware. La libreria que viene con el compilador es solo un interface para usar las rutinas del fabricante, y este interface incluye un metodo para ampliar el OpenGL para futuras mejoras de la libreria grafica (para usar OpenGL 1.0 se usa la misma libreria que para usar OpenGL 2.0) es lo que se llaman extensiones. Bastante mas adelante las veremos.

En resumen: no hace falta bajarse OpenGL de ningun sitio, y para hacer nuestro programa solo necesitaremos incluir "gl/gh.h" y "gl/glu.h" y añadir a nuestro proyecto las librerias opengl32.lib y glu32.lib. Para que nuestro programa funcione convendra tener bien instalado el driver de la tarjeta grafica, aunque al usar caracteristicas basicas de OpenGL va a funcionar siempre ya que windows lo puede ejecutar por software (si usamos caracteristicas avanzadas propias de nuestro hardware de video y no tenemos los driver, fallara).

Aunque este curso es eminentemente practico, no esta de mas la explicacion de algunos conceptos basicos en programacion grafica.

El buffer. No solo en graficos, sino en programacion general, es un espacio de memoria reservado para almacenar algo.
OpenGL usa varios bufferes. Los primeros y mas importantes son los que se usan para dibujar y el que se usa para presentar en pantalla.

Se usa la tecnica de doble buffer (pueden ser mas). Entender esto es la base para empezar a saber como trabaja un juego. ¡ Esto hay que entenderlo !.

En la tecnica de boble buffer, dibujamos en un buffer que no es visible para que no se aprecie como se va haciendo nuestro dibujo y mientras presentamos el dibujo anterior. Cuando hemos acabado de dibujar suicheamos (intercambiamos) un buffer por otro, y asi se presenta el dibujo instantaneamente. Despues seguimos dibujando en siguiente fotograma (frame), pero esta vez lo estaremos haciendo en el otro buffer, el que ahora esta oculto (back buffer), mientras el jugador esta viendo en pantalla el fotograma que hemos dibujado antes.
Estos bufferes son de memoria de video (la de la tarjeta de video, no memoria RAM del PC). El sistema no mueve la informacion de un buffer a otro, si no que, hace que la tarjeta de video cambie el rango de memoria de video que se traspasa al monitor, y el rango de memoria en la que esta el back buffer (el que se presenta en pantalla se llama front buffer).
Ejemplo:
Tenemos dos bufferes, el 1 y el 2 (por ejemplo). Que siempre van a ser el mismo trozo de memoria cada uno:

  • Ahora el 1 es el back y el 2 es el front. En el 1 pintamos mientras se ve el 2 en el monitor.
  • Intercambamos bufferes (SwapBuffers(DevContex))
  • Ahora el 2 es el back y el 1 es el front. En el 2 pintamos mientras se ve el 1 en el monitor con el dibujo anterior.
  • Intercambamos bufferes (SwapBuffers(DevContex))
  • Ahora el 1 es el back y el 2 es el front. En el 1 pintamos mientras se ve el 2 en el monitor.
  • ...

Por si hace falta la aclaracion, la animacion en un juego -y en una pelicula- se hace presentando un dibujo tras otro con pequeñas variaciones en el mismo. Siempre deben ser mas de 24 dibujos segundo para que el ojo no perciba el cambio y el movimiento sea suave.

Otro buffer importante es el de profundidad o z-buffer, muuuuuy importante si pretendemos hacer algo en 3D. En el se guarda la profundidad de cada pixel al pintarlo (distancia de lo que se pinta a la camara). Cuando se pinta de nuevo el mismo pixel de pantalla (con otro objeto) se compara la profundidad de el ultimo con la que estaba guardada de antes, si es mayor la nueva (esta mas lejos), no se pinta el pixel, y si es menor (esta mas cerca), se pinta el pixel del color nuevo en el buffer de dibujo (back buffer) y se pone la nueva profundidad -menor- en el buffer de profundidad.

Hay mas bufferes como el de estarcido (stencil) o el de acumulacion pero de momento no nos hacen falta.

Tras la explicacion, el codigo que creo que tiene suficientes comentarios para seguirlo facilmente.
En esta segunda version he puesto en funciones aparte la creacion de la ventana y la inicializacion de OpenGL.

#pragma comment( lib, "openGL32.lib" )  // Estas directivas del preprocesador
#pragma comment( lib, "glu32.lib" ) // le dicen a el enlazador (linker o
// linkador o ...) que incluya estas
// librerias. Esto se puede hacer en
// la configuracion del proyecto en el
// Visual C++ pero esto me parece mas
// claro.
 
#include <windows.h> // Fichero de inclusion para programas Windows
 
#include <GL/gl.h> // Los ficheros de inclusion de OpenGL.
#include <GL/glu.h> // Estrictamente solo es necesario el primero.
// El segundo es de funciones utiles que se podrian
// hacer de otra manera. No nos vamos a complicar la
// vida y la usaremos.
 
HINSTANCE IdAplicacion; // Para guardar el identificador del programa
HWND IdVentana; // Para guardar el identificador de la ventana
 
HDC DevContex; // Device Context para conectar la ventana con OpenGL.
HGLRC OGLrc; // Render Context para la ventana OpenGL.
 
int AnchoVentana = 600; // Lo que dice el nombre de la variable
int AltoVentana = 400; // Lo que dice el nombre de la variable
 
//--------------------------------------------------------------
// Funcion para inicializar OpenGL.
 
void IniciaGL()
{
// Definimos una estructura de tipo PIXELFORMATDESCRIPTOR para definir
// las caracteristicas graficas que queremos usar (dentro de las que nos
// permite el OpenGL de nuestra tarjeta de video)
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // Tamaño de este descriptor
1, // Número de versión
PFD_DRAW_TO_WINDOW | // El formato debe soportar ventana
PFD_SUPPORT_OPENGL | // El formato debe soportar OpenGL
PFD_DOUBLEBUFFER | // Debe soportar Doble Buffer
PFD_TYPE_RGBA, // También debe soportar RGBA
32, // Bits por pixels seleccionados
0,0,0,0,0,0, // Bits de color ignorados
0, // Sin buffer alpha
0, // Shift bit ignorado
0, // Buffer de acumulación ignorado
0,0,0,0, // Bits de acumulación ignorados
32, // Z-Buffer de 32 bits
0, // Sin buffer de pincel (Stencil)
0, // Sin buffer auxiliar
PFD_MAIN_PLANE, // Layer de dibujo principal
0, // Reservado
0,0,0, // Layers de máscara ignorados
};
 
// Estas funciones son las que hacen que la ventana acepte el modo grafico
// que queremos dado el descriptor de pixel anterior. (en el que uno de
// los parametros es que funcione con OpenGL).
 
DevContex=GetDC(IdVentana);
// Obtengo el "device context" de la ventana.

int PixF=ChoosePixelFormat(DevContex,&pfd);
// Busco un indice de una conbinacion

// que coincida con mis especificaciones
SetPixelFormat(DevContex,PixF,&pfd);
// Pongo la ventana en el formato que quiero.

// Dos funciones de OpenGL por fin (aunque sean especificas de windows).
OGLrc=wglCreateContext(DevContex);
// Indico a OpenGL que la ventana esta disponible para el.

wglMakeCurrent(DevContex,OGLrc);
// Le digo a OpenGL que nuestra ventana es donde tiene

// que dibujar a partir de ahora.
// A partir de ahora podemos considerar OpenGL inicializado.
// Aun nos queda decirle los parametros del modo en que dibujar, aunque
// eso se puede,
y de hecho se hace continuamente, cambiar durante la
// ejecucion del programa.
// Ahora pondremos algunos parametros puramente de OpenGL de inicio.
glClearDepth(1.0f);
// Profundidad del buffer de profundidad. Hace que lo que esta

// mas cerca se vea (dibuje) encima de lo que esta mas lejos.
glDepthFunc(GL_LEQUAL);
// Comparacion del buffer de profundidad.

glEnable(GL_DEPTH_TEST);
// Habilita test de profundidad. A partir de ahora, lo que

// esta mas cerca se pinta encima.
glClearColor(0,0,0,1.0f);
// Color del fondo. Color con el que se borra la pantalla, o la

// ventana donde pintamos. Cuando usemos la funcion glClear.
glShadeModel(GL_SMOOTH);
// Renderizado suave. Cuanta mejor calidad mas lento. Nosotros

// tenemos un ordenador reciente(+ o -) y queremos que se vea bien.
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
// Calidad buena de perspectiva.

glViewport(0,0,AnchoVentana,AltoVentana);
// Le dice a OpenGL el tamaño del lienzo en el que va a pintar en

// pixels. Coincidira, en principio, con el tamaño de nuestra ventana
// y empezando por 0,0 (la esquina de nuestra ventana).
 
 
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Borramos la ventana con el color antes establecido

// (en realidad solo el backbuffer (la pantalla oculta
// donde en realidad estamos pintando)
// y borramos tambien el buffer de profundidad
SwapBuffers(DevContex);
// Le digo a nuestro espacio de dibujo que cambie "muy rapido"
// el buffer de dibujo por el de visualizacion y viceversa
// (suichear en Espanglish)

 
} // Fin IniciaGL
 
 
//------------------------------------------------------------------
// Funcion para controlar lo que ocurre en la ventana segun los eventos
// que vienen de Windows. Mas tarde se asocia a la ventana que crearemos.
// De momento solo interceptamos la tecla ESC para poder salir del
// programa cuando queramos y el evento de destruccion de ventana con el
// que terminamos la aplicacion
LRESULT FAR PASCAL ProcesaMensajes(HWND Ventana, UINT Mensaje,
WPARAM wParam, LPARAM lParam)
{
switch(Mensaje) // Según el mensaje recibido
{
case WM_KEYDOWN: // Caso de mensaje de pulsación de una tecla
switch(wParam) // y segun el contenido de la variable wParam
{
case VK_ESCAPE: // ESC causa la salida del programa
// Funcion para enviar mensaje de cerrado a la ventana
// y despues de la aplicacion
PostMessage(Ventana, WM_CLOSE, 0, 0);
break;
}
break;
case WM_DESTROY:// Mensaje de destrucción de la ventana (provocado por
// nosotros al pulsar ESC o cerrar la ventana.
PostQuitMessage(0); // Funcion para salir del programa
break;
}
// Efectuar el proceso por defecto del mensaje (si viene cualquier mensaje
// que no hemos usado, querremos que haga lo que suele hacer)
return DefWindowProc(Ventana, Mensaje, wParam, lParam);
} // fin ProcesaMansajes
 
 
//------------------------------------------------------------------
// Funcion para crear la ventana de nuestro programa, que asociaremos
// al OpenGL para pintar en ella
void CreaVentana()
{
WNDCLASS ClaseVentana; // Declaramos un tipo de ventana, el nuestro.
 
//Definimos nuestro tipo de ventana...
ClaseVentana.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
// Se redibujara si

// cambia el tamaño de la ventana horizontal y verticalmente, y
// un solo DC (device context) para cada ventana (para cuando
// empezemos con OpenGL)
ClaseVentana.lpfnWndProc = ProcesaMensajes;
// La definimos antes para contolar

// los mansajes como los del teclado
ClaseVentana.cbClsExtra = 0; //Sin memoria extra para la clase
ClaseVentana.cbWndExtra = 0; //Sin memoria extra para la ventana
ClaseVentana.hInstance = IdAplicacion;
// Identificador del programa para asociar

// esta ventana con este programa.
ClaseVentana.hIcon = NULL; // De momento pasamos de icono
ClaseVentana.hCursor = LoadCursor(NULL, IDC_ARROW);
// Flecha normal de raton

ClaseVentana.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
// Por ahora lo ponemos blanco

ClaseVentana.lpszMenuName = NULL; // Pasamos de menu
ClaseVentana.lpszClassName = L"UnSitioWeb";
// Nombre de la clase (la "L" antes de la cadena es

// por que solo admite cadenas de caracteres
// unicode -LPCWSTR o TCHAR- y con la "L" se convierte).
 
RegisterClass(&ClaseVentana); // Registramos nuestro tipo de ventana
 
IdVentana = CreateWindowEx(
// Funcion que crea la ventana. Guardamos el identificativo.

WS_EX_APPWINDOW, // estilo extendido de ventana
L"UnSitioWeb",
// Nombre de la clase puesto antes (la "L" para UNICODE)

L"Un Sitio Web",
// Titulo que aparecera en la ventana.

WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX,
// Parametros de como sera la ventana

// se pueden combinar varios.
100, // Posicion Horizontal.
100, // Posicion Vertical.
AnchoVentana, // Ancho de la ventana.
AltoVentana, // Alto de la ventana.
(HWND) NULL, // No depende de otra ventana.
(HMENU) NULL,
// No le damos un menu diferente al de la clase(que es ninguno)

IdAplicacion,
// Identificador del programa al que pertenece la ventana. Al

// empezar lo guardamos en una variable para usarlo despues.
(LPVOID) NULL );
// Puntero a "no se que" datos (pasamos del tema, es para

// aplicaciones MIDI).
 
 
ShowWindow(IdVentana, SW_SHOW); // Mostramos nuestra ventana.
UpdateWindow(IdVentana);
// La actualizo para que muestre lo que tenga que mostrar
// (ahora nada)


}
// Fin CreaVentana

 
 
//------------------------------------------------------------------
// Funcion principal de un programa Windows (como main en C normal,
// aqui WinMain) el prorama empieza a ejecutarse a partir de esta funcion.
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER(lpszCmdLine);
// Para que no moleste al compilar, no lo usamos.

IdAplicacion = hInstance;
// Guardo el identificador del programa, luego lo usamos.

 
CreaVentana(); // Funcion que crea la ventana. Definida mas arriba.
IniciaGL(); // Funcion para inicializar OpenGL. Definida mas arriba.
 
 
// Este es el bucle habitual de windows que se esta ejecutando
// continuamente hasta que recibe el mensaje de acabar (ya lo hemos
// preparado en la funcion "ProcesaMensajes" asociada a

// la ventana, tambien asociada a este programa atraves de su
//
identificativo)
 MSG Mensaje; // Varible para contener los mensajes que van llegando.
while(TRUE) // Se ejecuta continuamente.
{
if(PeekMessage(&Mensaje, NULL, 0, 0, PM_NOREMOVE))
// Exploramos la cola de mensajes.

{// procesándolos adecuadamente
if(!GetMessage(&Mensaje, NULL, 0, 0))
return (int)Mensaje.wParam; // Terminamos.

TranslateMessage(&Mensaje);
DispatchMessage(&Mensaje);
}
else WaitMessage();
// en caso contrario esperamos un mensaje

}
 
} // fin WinMain
//------------------------------------------------------------------


Una ultima explicacion para los que investiguen por internet a cerca de programacion OpenGL. Existe una libreria muy usada, y la mas habitual en los ejemplos de codigo, llamada glut que simplifica el tema del teclado, inicializacion y demas aspectos del programa base.

Yo no la uso, tel vez por mania o por cabezoneria, pero prefiero gestionar yo mismo estos temas ya que no me parece tan complicado, como habeis visto, y te da mayor control sobre tu programa. En todo caso es una decision personal y el funcionamiento de OpenGL es el mismo en los dos casos. 

Suerte compilando.



¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
Jose Flores  - Otra forma para gestionar la ventana que usa OpenG   |201.241.12.xxx |25-06-2012 06:13:07
Para no tener que codificar usando las
librerias especificas de Microsoft y
hacer algo multiplataforma, les recomiendo que usen SFML para el manejo
de la
ventana y del teclado. Es MUY simple de usar.

Saludos desde Chile.
Vicengetorix   |87.221.234.xxx |30-06-2012 18:48:06
Efectivamente es una opción muy buena.
alfredo quintero  - ayuda   |190.74.222.xxx |24-11-2012 21:52:39
mira mi problema es que no se como se compila si me puedes ayudar
Vicengetorix   |87.221.204.xxx |26-11-2012 18:37:18
Lo más facil es bajarse de la zona de descargas el proyecto preparado para
poner el código y compilar.
Se llama "Proyecto de VC 2005
Express".
Aunque sea para VC2005 se puede abrir con una versión posterior y
lo convierte automáticamente.
Ahí podras ver las opciones que sirven.
JUAN JOSÉ   |Registered |22-10-2011 18:26:36
Perdón por la pregunta pero... ¿qué tiene que hacer este programita?

Me crea
la ventana en blanco y automáticamente se vuelve negra. ¿Va bien?.

No me da
errores pero sí que me dice varias veces en resultados que no encuentra o no
pudo abrir el archivo PDB.

Saludos.
Vicengetorix   |85.59.250.xxx |22-10-2011 23:40:14
Lo del archivo PDB, ni caso, son archivos de simbolos que genera VC pero no hace
falta.
El programa... pues es verdad que no hace nada visible, solo poner el
fondo negro de la ventana pero la inicializacion de OpenGL es necesaria para
pintar. En el siguiente capitulo si se dibuja ya algo.
edgar  - DUDA   |189.140.84.xxx |16-06-2011 01:29:44
Hola!
Felicitaciones por el sitio,
Una pregunta, apenas llevo unos meses
programando en openGL algunos Juegos, pero he notado que mientras agrego más
modelos, el programa se comienza a "alentar", como podría solucionar
esto??
Cabe mención que, tampoco utilizo GLUT, pues lo creo innecesario y
utilizo un iniGL() muy parecido al que haces referencia arriba.
Ojalá y puedas
responderme.
Vicengetorix   |85.53.211.xxx |29-06-2011 21:51:58
Hacen falta más datos para saber si es normal o no. Sigo en el foro.
carlos  - hola   |95.18.249.xxx |19-02-2011 23:37:10
sabes de algún curso que sea bajo mac os x, estoy utilizando tu código en un
proyecto c++ y me da errores pues no encuentra las cabeceras en la ruta que
indicas
Vicengetorix   |85.53.222.xxx |20-02-2011 14:08:29
Siento decirte que nunca he tocado un MAC. Debe haber buenas paginas en
internet.
Anónimo   |95.19.96.xxx |02-06-2010 19:07:41
¿Podrias explicar mejor donde conseguir todos los archivos necesarios y como
integrarlos en el proyecto?

Muchas gracias por el pedazo de tutorial!!!
Vicengetorix   |85.53.219.xxx |03-06-2010 12:51:42
No hace falta conseguir los archivos.
gl/gh.h ,gl/glu.h ,opengl32.lib y glu32.lib vienen con el SDK de Microsoft que ya hemos instalado.
Lo unico qure hay
que hacer es incluirlas en nuestro codigo, tal como
hacemos
en 
#include GL/gl.h
#include GL/glu.h
y

#pragma comment(lib"openGL32.lib")

#pragma comment(lib,"glu32.lib")

Estas dos ultimas se
puede hacer asi o incluirlas en la propiedades de proyecto. Esto me parece
mas facil.
Joyu  - Error cuando compilo   |188.87.251.xxx |15-02-2011 18:22:50
Hola, os he escrito 1 e-mail pero por si acaso lo pongo aquí también. Cuando
compilo me sale el siguiente error:
1>------ Operación Generar iniciada:
proyecto: 1, configuración: Debug Win32
------
1>Vinculando...
1>MSVCRTD.lib(crtexe.obj) : error LNK2019: símbolo
externo _main sin resolver al que se hace referencia en la función
___tmainCRTStartup
1>C:\Users\Josu\Doc uments\Visual Studio
2008\Projects\1\Debug\1.exe : fatal error LNK1120: 1 externos
sin resolver
1>El registro de compilación se guardó en el
"file://c:\Users\Josu\Documents 2;Visual Studio
2008\Projects\1\1\Debug\BuildL og.htm"
1>1 - 2 errores,
0 advertencias
========== Generar: 0 correctos, 1 incorrectos, 0 actualizados, 0
omitidos ==========
Si me podeis ayudar lo agradecería! Un saludo
Vicengetorix   |85.53.221.xxx |15-02-2011 19:43:49
En los comentarios del capítulo anterior surgió el mismo problema.
Creo que
tienes mal la configuracion del proyecto.
En las propedades de proyecto, opcion
LINKER apartado SISTEMA (o SYSTEM), hay una opcion llamada SUBSYSTEM (o
SUBSISTEMA)deberia poner "Windows
(/SUBSYSTEM:WINDOWS)" para que
reconozca el WinMain.
CarloCF  - Vaya C++   |190.42.104.xxx |15-08-2009 06:50:55
Saludos
Y felicitaciones por el esfuerzo que haces..eres un maestro. Despues de
ver el codigo salta a la vista por que C++ con funciones de retrollamada para
Windows es de lo mas complicado. Sin embargo eso parece a primera
vista...Despues de unos cuanto dias ya se ve mejor.
Interesante el curso...lo
busque por años y solo ahora lo encuentro Gracias nuevamente

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