¡Esta es una revisión vieja del documento!
Para programar desde C con OpenGL necesitarás linkar las librerías: opengl32.lib y glu32.lib. Necesitarás también incluir los encabezados: gl\gl.h (OpenGL en general) y gl\glu.h (utilidades).
hWnd = CreateWindow ( "NUESTRA_CLASE", "Ventana de ejemplo", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 500, 500, NULL, NULL, hInstance, NULL); // AQUÍ DEBERÍAMOS CONFIGURAR LA VENTANA PARA SOPORTAR OPENGL. While (!quit) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if ( msg.message == WM_QUIT ) quit = TRUE; else { TranslateMessage( &msg ); DispatchMessage( &msg ); } } else Render(); // Función que dibuja con OpenGL } return msg.wParam;
void setupogl(HWND hwnd) { int nPixelFormat; hDC=GetDC(hwnd); // hDC es un HDC // Asignamos los valores al formato de pixel PIXELFORMATDESCRIPTOR pfd; pfd.nSize = sizeof( pfd ); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; // Soporte para OGL y Doble buffer. pfd.iPixelType = PFD_TYPE_RGBA; // Color RGBA. pfd.cColorBits = 24; // De 24bits. pfd.cDepthBits = 16; // Buffer de profundidad. pfd.iLayerType = PFD_MAIN_PLANE; // Se dibujará en el plano principal. // Asignamos el nuevo formato de pixel a la ventana nPixelFormat = ChoosePixelFormat (hDC, &pfd); SetPixelFormat (hDC, nPixelFormat, &pfd); // Creamos el contexto para OpenGL (indicamos que será OpenGL el encargado de dibujar sobre la ventana). HGLRC hRC=wglCreateContext(hDC); // Crea y activa el contexto de generación wglMakeCurrent(hDC, hRC); // Limpiamos con el color escogido glClearColor (0.0f,0.0f,0.0f,1.0f); }
Necesitamos configurar el sistema de coordenadas de nuestra ventana, para ello lo que hacemos es:
glViewport, con esta indicamos la porción donde trabajaremos, es decir, será el lugar donde se vaya renderizando, si este es cuadrado el render será cuadrado, si es rectangular el render será rectangular.void ConfiguraTamano (GLsizei w, GLsizei h) { if (h<=0) h=1; if (w<=0) w=1; glViewport (0,0,w,h); glMatrixMode (GL_PROJECTION); glLoadIdentity(); glOrtho (0,w, 0,h, 100, -100); glMatrixMode (GL_MODELVIEW); glLoadIdentity(); }
Llamaríamos a esta función desde el mensaje WM_SIZE del WndProc de la siguiente forma:
ConfiguraTamano(LOWORD(lParam), HIWORD(lParam));
Cuando se muestra la ventana por primera vez se genera un mensaje WM_SIZE con el tamaño de esta, por lo que no sería necesario llamar a esta función al inicio del programa. Además si la ventana cambia de tamaño necesitaremos reiniciar nuestro sistema de coordenadas, por lo que la llamada desde WM_SIZE no es mala opción.
Para ello necesitamos guardar el tamaño con el que se crea la ventana (ALT y ANC). Necesitamos también el tamaño de la vista en pixels (RangoXYZ) y el alto y ancho actuales (NewALT y NewANC):
const float ALT=500.0, ANC=500.0; GLfloat RangoXYZ=5.0f; GLfloat NewALT, NewANC;
La clave está en la función ConfiguraTamano, esta realiza una regla de 3 con el rango inicial: Si para la altura inicial equivalía a RangoXYZ, cuanto equivale para la altura actual?.
void ConfiguraTamano (GLsizei w, GLsizei h) { float NewRangoX=(RangoXYZ*w)/ANC; float NewRangoY=(RangoXYZ*h)/ALT; NewALT=NewRangoY; NewANC=NewRangoX; if (h<=0) h=1; if (w<=0) w=1; glViewport (0,0,w,h); glMatrixMode (GL_PROJECTION); glLoadIdentity(); glOrtho (-NewRangoX, NewRangoX, -NewRangoY, NewRangoY, -RangoXYZ, RangoXYZ); glMatrixMode (GL_MODELVIEW); glLoadIdentity(); }
glClear(GL_COLOR_BUFFER_BIT);. Llamandolo al inicio del dibujo, borra la vista mostrando un color plano. Podemos introducir también: GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT lo que borrará el buffer de profundidad. Podemos decir con qué color se hará el borrado mediante glClearColor.SwapBuffers( hDC );. Llamandolo al final dibujo, imprime los contenidos del buffer.glColor3f(x.xf, x.xf, x.xf);. Antes del dibujo de una primitiva, elige su color. El 3 de su nombre significa los colores RGB y puede ser intercambiado por 4 para colores RGBA. La f corresponde al tipo de datos (i (integer), b(byte), ub (unsigned byte)…) es decir que si hacemos glColor3ub podemos usar la nomenclatura de 256 bytes para el color R(0…255), G(0…255), B(0…255).glEnable(elemento); y glDisable(elemento);. Activa o desactiva el elemento que se pasa por parámetro, si luego queremos saber si ese parámetro está activo tendremos que pasarselo a glIsEnabled(elemento);. El parámetro puede ser cualquiera de los siguientes tipos ya definidos:glFrontFace(GL_CCW) o no glFrontFace(GL_CW). Por defecto se dibuja en sentido contrario a las agujas del reloj.glShadeModel(GL_FLAT);. Define como se pintarán las primitivas:GL_FLAT con un color plano.GL_SMOOTH pinta a partir de los vertices (dando colores a los vertices) mediante un suavizado.glPolygonMode(r,s);. Activa que polígono mostrará:gluLookAt Corresponde a la camara. Tiene 9 parametros GLdouble:// Ejemplo 1: gluLookAt(0.0, 1.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Ejemplo 2: gluLookAt(0.0,0.0,-10.0, // como si hiciesemos un glTranslatef(0.0,0.0,-10.1) desde 0.0 0.0,0.0,0.0, // al lugar donde apunta 0.0,1.0,0.0);
glColorMask (BOOL,BOOL,BOOL,BOOL); Cada booleano corresponde a un valor RGBA. Aplica una máscara a la figura. Por ejemplo un cuadrado verde que atraviesa un cuadrado rojo, si todo es true excepto el valor de rojo el cuadrado se volverá amarillo al pasar por lo rojo. Por defecto todos los valores son TRUE para que puedan visualizarse todos los colores en la escena.
Si no está activado las caras traseras pueden llegar a dibujarse sobre las delanteras, es decir, las superficies que no se deberían de dibujar se dibujan.
Para activarlo haremos una llamada a glEnable pasandole GL_DEPTH_TEST:
glEnable (GL_DEPTH_TEST);
Una vez hecho esto necesitaremos, al repintar la escena y borrarla previamente llamando a glClear indicar también que se borre dicho buffer:
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Para indicar con qué valor (entre 0 y 1) limpiaremos el buffer de profundidad utilizaremos la función glClearDepth. Por defecto su valor es 1.0f.
glClearDepth(1.0f);
En OGL las funciones glVertex3f, glVertex2f… Se utilizan para especificar un punto en el área de dibujo, la agrupación de un número de estos puntos forman las primitivas (polígonos).
glVertex3f(valorxf, valoryf, valorzf); glVertex2f(valorxf, valoryf); // glVertex2f asume que el valor z es 0.0f
glBegin y otra a glEnd, en medio de estas dos indicaremos tantos vertices (puntos) como la primitiva que dibujemos requiera. A glBegin se le pasa por parámetro el identificador del tipo de primitiva.glBegin(GL_POINTS); glVertex2f(0.0f, 0.0f); glEnd();
glPointSize(número); que no es necesario que vaya entre el glBegin ni el glEnd.glLineWidth(número); y un formato mediante glLineStipple(variableint, 0x5555); siendo la variable int el patrón de formato (para activar el formato tendremos que hacer una llamada a glEnable(GL_LINE_STIPPLE);.glFrontFace(GL_CW). Si queremos volver a la normalidad le pasaremos el parámetro: GL_CCW.glBegin(GL_LINE_STRIP); for (i=0.0f; i<=(2.5f*GL_PI); i+=0.1f) { x=0.5f*sin(i); y=0.5f*cos(i); glVertex2f(x,y); } glEnd(); // Siendo GL_PI=3.14
Las transformaciones transformaciones las podemos hacer sobre:
OpenGL utiliza matrices para manejar los datos, existen tres tipos\modos y la elección de este “modo de matriz” se lleva a cabo según el parámetro que le pasemos a glMatrixMode:
gluLookAt) se utiliza en la MODELVIEW): glLoadIdentity, gluPerspective, glFrustum, glOrtho, gluOrtho2.
* GL_TEXTURE: Pila de matrices de textura (coordenadas de textura).
==== Matriz del modelador (o matriz general) ====
Es la matriz sobre la que se trabaja inicialmente y a partir de la que se cran las demás matrices. Podemos hacer distintos tipos de transformaciones:
* Movimiento con las funciones tipo glTranslatef(X,Y,Z);
* Rotacion con las funciones tipo glRotatef(Angulo, X, Y, Z);
* Escalado con las funciones tipo glScalef(X,Y,Z);. Esta función no hace nada si se le llama como glScalef(1,1,1);, ya que deja tal cual los polígonos, pero los puedes escalar a la mitad llamandola glScalef(0.5,0.5,0.5); o invertirla verticalmente glScalef(1,-1,1);
Para reiniciarla, es decir, antes de empezar a dibujar la escena (piena que esta matriz se “deforma” al ir dibujando y colocando los elementos) llamaremos a glLoadIdentity() que carga la matriz identidad (la inicial). Por lo tanto, para mover dos figuras hacia distinto lugar, aunque generalmente se utiliza la pila de matrices también podemos, primero mover una con glTranslatef y antes de mover la otra reiniciaremos la matriz con glLoadIdentity(); y luego moveremos la otra.
==== Pila de matrices ====
El concepto consiste en, teniendo una matriz inicial añadida a la pila, añadimos una segunda matriz que será una copia de la anterior hacemos las transformaciones necesarias sobre esta y dibujamos, luego podemos volver a añadir otra matriz (que sería una copia de la anterior) o sacar esta de la pila.
Usar el tipo de matriz GL_MODELVIEW.
* Para guardar el estado actual de la matriz y crear otra matriz: glPushMatrix();
* Para cargar el último estado guardado: glPopMatrix();
<code cpp>
glLoadIdentity(); Cargamos la matriz identidad
glPushMatrix(); Añadimos una matriz a la pila
glTranslatef(0.5, 0.0, 0.0); Moveremos lo que dibujemos a la derecha
glBindTexture(GL_TEXTURE_2D, this→texture);
drawCuad();
glPushMatrix(); Añadimos una segunda matriz a la pila
glTranslatef(0.0, 0.5, 0.0); Moveremos lo que dibujemos hacia arriba, es decir, estará arriba-derecha
glBindTexture(GL_TEXTURE_2D, this→texture);
drawCuad();
glPopMatrix(); Sacamos la segunda matriz, lo dibujado queda a la derecha
glPopMatrix(); Sacamos la primera matriz, lo dibujado queda en el centro
glPushMatrix (); Añadimos una matriz
glTranslatef(-0.5, 0.0, 0.0); Lo que dibujemos a partir de ahora quedará a la izquierda
glBindTexture(GL_TEXTURE_2D, this→texture2);
drawCuad ();
glPopMatrix();
</code>
Otro ejemplo:
<code cpp>
GlClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
GlMatrixMode (GL_MODELVIEW); Modelaremos la matriz del modelador.
glRotatef(1.0f,0.0f,0.1f,0.0f); Se rotará todo el dibujo durante todo el rato.
glPushMatrix(); Se crea una matriz.
glRotatef(150.0,0.0f,0.1f,0.0f); Se rota lo que hay en la matriz hasta el punto elegido, como no hay una variable que se incremente sólo se efectuará una vez.
glPushMatrix(); Se crea otra matriz
glRotatef(-30.0f,1.0f,0.0f,0.0f); Lo mismo que en la matriz anterior, colocaremos los elementos.
glPushMatrix(); Otra matriz para la cola
glRotatef(xse,0.0f,0.1f, 0.0f); Esta rotación se efectuará mientras “xse” vaya cambiando.
DibujaCuadrao (0.0f,0.0f,0.0f,0.5f);
glPopMatrix(); Cerramos la última matriz abierta.
DibujaSuelo();
glPopMatrix(); Cerramos segunda matriz abierta.
glPopMatrix(); Cerramos primera matriz abierta.
SwapBuffers( hDC );
Trans=0.0f;
xse+=0.9f;
</code>
En el siguiente ejemplo se nos muestra dos cubos, uno rotando en su eje de las X y el otro en el suyo de las y. En el que rota en el eje de las y, si no pusiesemos ese glTranslatef el cubo no giraría sobre sí mismo, sino sobre el eje y(0). Al crear una nueva matriz tenemos que mover el punto 0 y rotarla. Así sí que rota sobre sí mismo.
<code cpp>
glPushMatrix();
glRotatef(-XRot,1.0f,0.0f,0.0f);
CreaCubo(-4.5f,0.0f,0.0f,2.0f);
glPopMatrix();
glPushMatrix();
glTranslatef(4.5f,0.0f,0.0f);
glRotatef(XRot,0.0f,1.0f,0.0f);
CreaCubo(0.0f,0.0f,0.0f,2.0f);
glPopMatrix();
</code>
==== Manipulación de matrices ====
* glLoadMatrix(matriz); → Siendo matriz un vector de este tipo:
<code cpp>
GLfloat matriz[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
</code>
Carga una matriz creada por nosostros.
* glMultMatrix(matriz); → Multiplica la matriz actual por la matriz especificada.
===== Proyecciones =====
==== glOrtho ====
En el modo de coordenadas ortogonal modo no existe profundidad y los objetos no están dispuestos como si hubiese un punto de fuga. Es decir, los más cercanos no se dibujan más grandes que los más lejanos sino que todos se dibujan con el mismo tamaño. El control de coordenadas se hace definiendo la vista con una llamada a glOrtho.
Su sintaxis es: glOrtho(-x,x,-y,y,-z,z); y estos parámetros significan:
* Bits que hay a la izquierda del punto 0,0.
* Los que hay a la derecha.
* Los que hay desde abajo.
* Los que hay desde arriba.
* Desde el usuario a ese punto (0,0).
* Desde el punto (0,0) al fondo);
<code cpp>glOrtho (-3.0f, 3.0f, -3.0f, 3.0f, -3.0f, 3.0f);</code>
En este ejemplo el centro de la vista estará en el centro de la ventana. Habrán 3.0 “pixels” desde un lado al centro. O desde arriba… O 3.0 de profundidad desde el usuario a la vista y desde la vista al fondo de la ventana.
<code cpp>glOrtho (0, 3.0f, 0, 3.0f, 0, 3.0f);</code>
En este el centro de la vista estará en la esquina derecha de la parte de debajo de la ventana (Para OpenGL ese es el punto (0,0)). Sin haber ningún pixel de profundidad entre el usuario y la vista pero desde la vista al fondo de la ventana habrían 3.
==== Proyeccion en perspectiva ====
Esta proyección se consigue sustituyendo el glOrtho (proyección ortogonal) por gluPerspective (habiendo hecho el include a <gl\glu.h>).
gluPerspective tiene como parámetros:
* angulo visual vertical, a más pequeño más será la relación cerca-grande de los objetos (si está muy bajo, sería como si mirases con unos primáticos).
* GLfloat que representa la proporción de altura con anchura (ancho de la vista entre alto).
* Comienzo de la vista
* Fin de la vista
<code cpp>
GLfloat fAspect = (GLfloat)w/(GLfloat)h;
gluPerspective(60.0f,fAspect,1.0,40.0);
…
Otro ejemplo
gluPerspective(60.0, fAspect, 1.0, 500.0);
</code>
Ver relación con la cámara.
Cuando iniciamos el programa y lo configuramos en perspectiva, deberemos definir la cámara con gluLookAt. Si la ventana donde estamos trabajando se reinicia deberemos volver a configurar la perspectiva. Aquí el código para reiniciar la vista:
<code cpp>
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(this→angle, (float)w / (float)h, 0.1f, lengthView + 0.1f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0f, 0.0, -0.2f, 0.0f, 0.0f, lengthView, 0.0f, 1.0f, 0.0f);
</code>
===== Iluminación =====
Para usarla debemos activarla con un glEnable(GL_LIGHTING);.
==== Tipos de luces ====
Un material puede reflejar tres tipos de luces:
* Ambiental: La luz de la escena, no viene de ningun sitio en particular.
* Difusa: Es reflejada por el objeto en distintas direcciones.
* Especular: Es reflefada por el objeto en una direccion uniforme, como puede ser la del reflejo en un metal que crea algo así como un destello o brillo.
==== Configuración de los materiales ====
La base de la iluminación es el cómo los materiales reflejan la luz. Existen dos formas de definir este “cómo”:
* Con glMaterial, que se ha de llamar antes de mostrar un polígono o grupo de polígonos y tendrá efecto hasta que otra llamada a glMaterial sea hecha. Recibe por parámetros:
* Qué caras del polígono reflejan la luz: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK
* Qué tipo de luz: GL_AMBIENT_AND_DIFFUSE, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR
* Un array RGBA que indica la cantidad.
<code cpp>
Glfloat gray[] = { 0.75f, 0.75f, 0.75f, 1.0f };
…
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);
glBegin(GL_TRIANGLES);
glVertex3f(-15.0f,0.0f,30.0f);
…
</code>
* Para utilizar la otra forma (preferible) llamaremos a la función glEnable con el parámetro GL_COLOR_MATERIAL. Esto activa el que los materiales reflejen la luz indicada por glColor. Podemos definir qué tipos de luz reflejan los materiales con la función: glColorMaterial (donde, tipo de luz>);
* donde: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK → Las caras que han de reflejarla.
* tipo de luz: GL_AMBIENT_AND_DIFFUSE, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR.
<code cpp>
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
…
glcolor3f(0.75f, 0.75f, 0.75f);
glBegin(GL_TRIANGLES);
glVertex3f(-15.0f,0.0f,30.0f);
…
</code>
==== Luz ambiental ====
Una vez activado el GL_LIGHTING debemos definir la luz ambiental de la escena y pasarsela a la función glLightModelfv indicandole que realmente es el modelo ambiental el que vamos a usar.
- Definimos el color de la luz ambiental: GLfloat LuzAmbient[]={1.0f,1.0f,1.0f,1.0f}; → La luz por defecto, blanca (lo refleja todo).
- Se la pasamos a la función glLightModelfv indicandole que es la luz ambiente (GL_LIGHT_MODEL_AMBIENT): GlLightModelfv(GL_LIGHT_MODEL_AMBIENT, LuzAmbient);
- Si hemos configurado los materiales correctamente podremos ver la escena como anteriormente la habíamos visto.
==== Normales ====
Una normal es un vector en posición de línea perpendicular (90º) sobre una superficie. Aunque podemos indicar la normal del polígono generalmente las indicaremos por cada vértice de este (hacerlo por polígono sería como si este correspondiese a una porción de una superficie lisa).
Por ejemplo imaginemos un plano que está plano en una posición (x,1,0), un vértice podría estar pefectamente en la (1,1,0), pues una normal a este vertice sería un vector desde este al (1,10,0). Para especificar un vector necesitamos dos puntos, el inicio y el final, la normal podemos especificarla restando los dos vértices, es decir, esta normal sería: (0,9,0).
Esta normal también es llamada “la normal transladada”.
Para indicar una normal, antes de indicar el vértice llamaremos a glNormal3f.
Para encontrar la normal de un vértice (normalizar ese vértice), lo que se hace es hacer el producto cruzado con los otros dos vértices contiguos.
=== Normalizar ===
Para un triangulo de 3 puntos (p1, p2 y p3). Para el vector U = p2 - p1 y para el vector V = p3 - p1, el vectro normal sería N = U x V calculado como:
<code cpp>
void findNormal (floa result[3], float point1[3], float point2[3], float point3[3]) {
GLfloat v1[3],v2[3];
v1[0] = point1[0] - point2[0];
v1[1] = point1[1] - point2[1];
v1[2] = point1[2] - point2[2];
v2[0] = point2[0] - point3[0];
v2[1] = point2[1] - point3[1];
v2[2] = point2[2] - point3[2];
result[0] = v1[1]*v2[2] - v2[1]*v1[2];
result[1] = -v1[0]*v2[2] + v2[0]*v1[2];
result[2] = v1[0]*v2[1] - v2[0]*v1[1];
}
</code>
Hay que tener especial cuidado en el orden de cálculo, sobretodo si los polígonos han sido dibujados en el orden de las agujas del reloj o no.
=== OpenGL y las normales ===
Para que OpenGL pueda usa aplicar la normal todas las normales que indiquemos deberán ser vectores unidad (unit normal), esto es, una normal que tenga longitud 1. Para saber qué longitud tiene un vector buscaremos su magnitud, si queremos conseguir un vector unidad únicamente deberemos dividir cada coordenada del vector entre su magnitud, el vector resultante apuntará al mismo sitio pero tendrá longitud 1.
Aún así, podemos hacer que OpenGL convierta todas las normales a vectores unidad automáticamente utilizando glEnable(GL_NORMALIZE);. Pero esto tiene un problema, si las figuras a las que hemos calculado las normales son escaladas las normales cambiarán, debemos decir a OpenGL que también active el reescalado automático de las normales mediante una función alternativa: glEnable(GL_RESCALE_NORMALS);.
Es decir, lo mejor es indicar, al crear un vértice, la unit normal y activar GL_RESCALE_NORMALS.
==== Crear fuentes de luz ====
También podemos crear una fuente de luz. OpenGL tiene ocho fuentes de luz disponibles (desde GL_LIGHT0 a GL_LIGHT7 (el máximo de luces lo indica la constante GL_MAX_LIGHTS)). Antes de activar cada una de ellas tendremos que configurarla con: glLightfv(luz, parámetro de iluminación, vector con los datos necesarios);
* luz: Desde GL_LIGHT0 a GL_LIGHT7.
* Parámetro de iluminación: GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_POSITION (para la posición) ← Cuatro valores (RGBA), excepto para GL_POSITION en la que 1.0f significa que está en el infinito positivo… Y estas de un solo valor → GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRIC_ATTENUATION.
* Datos: Los guardamos en un array de floats: GLfloat lightPos[] = {-50.f,50.0f,100.0f,1.0f};
<code cpp>glLightfv(GL_LIGHT0, GL_POSITION, lightPos);</code>
Luego tendremos que activarla explícitamente con glEnable (GL_LIGHT0);
<code cpp>
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f};
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0,GL_SPECULAR,specular);
glEnable(GL_LIGHT0);
</code>
==== Luz especular ====
Para darle a una luz las propiedades especulares se hace de la misma forma que con las otras luces. En el ejemplo anterior sería: glLightfv(GL_LIGHT0, GL_SPECULAR, <array de floats>);
Para que se produzca reflectancia en los materiales tenemos que tener activo GL_COLOR_MATERIAL, pasandole los parámetros GL_FRONT y GL_AMBIENT_AND_DIFUSE.
* glMaterialfv → Produce la superficie que refleja la luz especular.
* glMateriali → Desde aquí indicamos el brillo del material. Parámetros: Desde donde, brillo (GL_SHININESS), cantidad (entero).
<code cpp>
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
glMateriali(GL_FRONT, GL_SHININESS, 5);
</code>
==== Focos ====
El crear un foco de luz se hace de la misma forma que crear una luz. Debemos de codificar un array de floats para la posición, otro para la luz difusa y otro para la dirección. Lo tratamos como a una luz normal y corriente, es decir, que le pasaremos estos valores mediante la función glLightfv. Luego le pasaremos a esta luz los parámetros para GL_SPOT_CUTOFF y para GL_SPOT_EXPONENT, el primero especifica el angulo de luz y el segundo su grado de brillo. GL_SPOT_DIRECTION también es necesario, indica la dirección del foco. Luego habilitaremos la luz que acabamos de crear.
<code cpp>
glLightfv(GL_LIGHT0, GL_POSITION,FocPos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, FocLuz);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, FocDir);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 90.1f);
glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 100.0f);
glEnable (GL_LIGHT0);
</code>
==== Objetos brillantes y movimiento de luces ====
Para hacer que una luz tenga movimiento, dentro de algún cambio de una matriz (entre el glPushMatrix y el glPopMatrix) haremos una llamada a la declaración de la posición de la luz: glLightfv(GL_LIGHT0, GL_POSITION,FocPos);
Para hacer que un objeto brille hemos de entender que algo brillante se compone de una figura y una luz (por separado), pero si ponemos una luz en la misma posición que un objeto el objeto la tapará. Lo que tenemos que hacer es refrescar la luz, ponerla en su posición, y luego deshabilitar sus atributos. Luego dibujariamos el objeto y al final acabaríamos rehabilitando la luz. Para ello imaginaremos una función render, recolocamos la luz, y ahora deshabilitamos sus propiedades con glPushAttrib(GL_LIGHTING_BIT); dibujamos la figura y reactivamos las propiedades de la luz con glPopAttrib();
* Para el calculo automático de las normales: glEnable(GL_AUTO_NORMAL); y glEnable(GL_NORMALIZE);
* Se produce un efecto al rotar un objeto con una luz al lado, este es que la luz rota también. Para que esto no pase debemos refrescar la posición de la luz antes de rotar el objeto: glLightfv(GL_LIGHT0, GL_POSITION,FocPos);
===== DisplayLists =====
Las DisplayList o 'listas de visualización' son figuras, vértices, polígonos… (código ogl) que se guardan en la memória de la tarjeta gráfica con un identificador y de esa forma las mostramos (ejecutamos)de una forma más rápida y óptima.
==== Creación de una lista ====
<code cpp>
glNewList (entero, modo);
… código…
glEndList();
</code>
Con el código que hay entre glNewList y glEndList creamos una lista y le damos un índice, el número entero, y un modo:
* GL_COMPILE para que sólo se compile y para ejecutarla sólo cuando la llamemos.
* GL_COMPILE_AND_EXECUTE para que se compile y se ejecute.
El código puede ser un modelado (llamadas a vértices, polígonos…) o también llamadas a otras listas.
Podemos definir un nombre como entero y luego usarlo en la lista de visualización cómo índice:
<code cpp>
#define cabeza 1
…
glNewList(cabeza, GL_COMPILE);
</code>
==== Creación de varias listas ====
Utilizando glGenLists podemos reservar varios identificadores de listas seguidos (luego se cargarán con funciones como wglUseFontOutlines). El formato es:
<code cpp>GLuint glGenLists (GLsizei range)</code>
Reserva tantos nombres como range se han indicado, el número del primero será el que devuelva.
==== Llamada a listas ====
Podemos hacerlo de varias maneras con la función glCallList(indice de la lista). Usando con lo que usando el código anterior sería: glCallList(cabeza) o también glCallList(1). Cuando llamemos a una lista se ejecutará todo el código que ésta contiene y se mostrará la figura que forma.
Otra forma es utilizar glCallLists que llama a una serie de listas en las que sus indices están en una matriz:
<code cpp>
…
int lists[100];
…
for (i=1; i⇐100; i++) { lists[i]=i+1; }
…
glNewList(lists[0], GL_COMPILE); … glEndList();
glNewList(lists[1], GL_COMPILE); … glEndList();
…
glCallLists(50, GL_INT, lists);
…
</code>
Así llamamos a las cincuenta primeras listas de la matriz “lists”. Como la matriz contiene integers el segundo parámetro de la función es GL_INT, podría ser GL_FLOAT, GL_BYTE…..
=== glListBase ===
Cuando hacemos una llamada a glCallLists indicamos las listas que se han de mostrar a partir de su identificador que es un número escogido a partir del 0, pero a veces queremos llamar a las listas a partir de un número, por ejemplo a partir del número que le fue asignado a la primera. Ese es el caso, por ejemplo, de las fuentes bitmap que cuando se generan se hace a partir de un número de lista que no tiene por qué ser el 0 pero la llamada a estas sí que se hace como si lo fuese. glListBase asigna el número escogido que se utilizará como “base” al llamar a glCallLists.
Para que no quede esta base para siempre haremos la llamada a glListBase entre un glPushAttrib y un glPopAttrib de GL_LIST_BIT.
<code cpp>
glPushAttrib(GL_LIST_BIT);
glListBase(font - 32);
glCallLists(strlen(s), GL_UNSIGNED_BYTE, s);
glPopAttrib();
</code>
==== Borrado de listas ====
glDeleteLists borra un rango de listas. Hay que pasarle dos parámetros: la primera lista que borrará y la última. Todas las listas que se encuentren en este rango serán borradas: glDeleteLists (1,50);
La función glIsList retorna un booleano que indica si esa lista existe. El parámetro que le pasamos es, evidentemente, el índice de la lista.
===== Texturas =====
==== Texturas simples ====
Aplicar una textura a un polígono es colocar una imágen sobre él. En OpenGL podemos tener texturas de 1d, 2d y 3d, pero básicamente trabajaremos con las de 2d. El tamaño de estas texturas no importa, pero es necesario que el tamaño de sus lados sean potencias de 2. Podemos tener, por ejemplo, texturas de 128×64 (2^7)x(2^6), de 256×256…
Para utilizarlas lo primero que necesitaremos es una variable unsigned int para cada una de ellas, en esta OpenGL guardará su identificador. Necesitaremos, evidentemente, cargar los datos del archivo imagen correspondiente a la textura y habilitar GL_TEXTURE_2D mediante glEnable. Utilizaremos las siguientes funciones:
* glGenTextures(nTexturas,&idTextura); Genera un número nTexturas de nombres para la variable idTextura (el unsigned int anterior y que puede ser un array).
* glBindTexture(GL_TEXTURE_2D, idTextura); Enlaza una textura 2d al idTextura indicado. Hay que llamar a esta función por cada textura que tengamos.
* glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); Indica a OpenGL el filtrado deseado para las texturas.
* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imageWidth , imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData); Especifica el tamaño, tipo, localización y formato de la textura.
Una vez generada la textura para seleccionarla utilizaremos glBindTexture.
Podemos tener texturas de 1d (un pixel)(GL_TEXTURE_1D), 2d (con alto y ancho)(GL_TEXTURE_2D) y 3d (GL_TEXTURE_3D). Estos parámetros podremos pasárselos a estas funciones. === Texturas 2d y 1d ===
* Para las texturas 2d usamos la función glTexImage2d, a la que hay que pasarle los siguientes parámetros:
* GL_TEXTURE_2D.
* El nivel, especifica las diferentes resoluciones. Como sólo usaremos 1 le daremos el valor 0. Echale un vistazo a los Mipmaps para más información de niveles.
* El formato interno del color, cómo lo trataremos: GL_RGB, GL_RGBA, GL_LUMANANCE y GL_LUMANANCE_ALPHA.
* La anchura y la altura de la textura.
* El borde, 0 para que no tenga y 1 para que sí.
* El formato de los datos que vamos a introducir(GL_COLOR_INDEX, GL_RGB, GL_RGBA, GL_RED, GL_GREEN…).
* El formato de los datos con los que tenemos guardada textura (por ejemplo GL_UNSIGNED_BYTE).
* Los datos de la textura.
Para texturas 1d usaremos la función glTexImage1d. Los parámetros para esta función son los mismos que para la función anterior, únicamente cambia que no le indicamos la altura, ya que sólo es de un pixel.
Para ver como utilizar el alpha de una textura mira transparencias.
=== Filtrado de textura (Texture Filtering) ===
Consiste en decir a OpenGL cómo actuar respecto a las texturas que se apliquen a un polígono que va “deformandose”, para ello utilizamos la función glTexParameteri, con los parámetros:
* Objetivo, al tipo de textura: GL_TEXTURE_2D o GL_TEXTURE_1D
* Nombre, GL_TEXTURE_MIN_FILTER (para el valor mínimo), GL_TEXTURE_MAG_FILTER (para el valor máximo), GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, o GL_TEXTURE_PRIORITY.
* Valor,
* GL_NEAREST
* GL_LINEAR
* GL_NEAREST_MIPMAP_NEAREST
* GL_NEAREST_MIPMAP_LINEAR
* GL_LINEAR_MIPMAP_NEAREST
* GL_LINEAR_MIPMAP_LINEAR
<code cpp>
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
</code>
=== Diferentes texturas en diferentes objetos ===
Imaginemos que tenemos dos objetos y vamos a texturizarlos con diferentes texturas (texture[0] y texture[1]). Las cuales generaremos así:
<code cpp>
glGenTextures (2, texture); 2 el valor del array y no las referenciaremos
glBindTexture (GL_TEXTURE_2D, texture[0]); Y a partir de aquí cambiaremos los valores para la textura [0].
glBindTexture (GL_TEXTURE_2D, texture[1]); Haremos los cambios para la textura [1].
</code>
Ahora para aplicarle la textura a un objeto eligiremos que textura será la que aplicaremos (con glBindTexture) y le daremos las coordenadas.
=== Coordenadas de textura ===
Una vez tengamos las texturas definidas, ahora debemos de indicar las coordenadas que tendrá la textura. En un cuadrado estas coordenadas son:
* Parte alta izquierda: 0.0,1.0
* Parte baja izquierda: 0.0, 0.0
* Parte baja derecha: 1.0, 0.0
* Parte alta derecha: 1.0, 1.0
Por lo tanto, si a estos valores damos parte alta izquierda (ai) 0.25 0.75, ad 0.75 0.75, bi 0.25 0.25, bd 0.75 0.25, veremos sólo el centro de la textura.
Para aplicar estas coordenadas usaremos la función glTexCoord2f de la siguiente forma:
<code cpp>
glBegin(GL_QUADS);
glTexCoord2f (0.0f,0.0f); glVertex3f(-3.0f, 4.0f, 0.1f);
glTexCoord2f (1.0f,0.0f); glVertex3f(-2.0f, 2.0f, 0.1f);
glTexCoord2f (1.0f,1.0f); glVertex3f(-4.0f, 2.0f, 0.1f);
glTexCoord2f (0.0f,1.0f); glVertex3f(-5.0f, 4.0f, 0.1f);
glEnd();
</code>
* Si quisiesemos poner una imagen de fondo y sobre ella las figuras 3d texturizadas deberemos desabilitar el uso de las texturas (glDisable(GL_TEXTURE_2D);) antes de dibujarla y una vez dibujada volverlas a abilitar.
=== Adquisición del color del objeto ===
La textura puede ser mezclada con el color del polígono al que se le asigna, esto es llamaado “el ambiente” de la textura. La función glTexEnvi se utiliza para indicar dicho ambiente, sus parámetros son:
* GL_TEXTURE_ENV
* GL_TEXTURE_ENV_MODE
* Cómo afecta el color:
* GL_MODULATE, a color más claro en la textura más color que se deja ver del polígono. Si utilizamos texturas con alpha este flag será necesario.
* GL_DECAL, hace que el color de la textura se sobreponga al del polígono (como GL_REPLACE)
* GL_BLEND, hace que los puntos blancos de la textura hagan que esa parte del polígono se transparente, los negros que se vea. Es decir, la textura actua como alfa sobre el polígono.
=== Uso de mipmaps ===
Los mipmaps son colecciones de imágenes que ayudan a OpenGL a renderizar cuando el modelo donde la textura está cambia de tamaño, agilizando los cálculos para esta. La técnica se basa en que en una misma imágen existan distintos tamaños de la textura, así cuando el modelo se aleja se escoge una textura más pequeña y cuando se acerca una más grande, evitando así que se vayan haciendo siempre cálculos sobre la textura.
Así pues para crearlas deberíamos, primero crear distintos archivos de imágenes de distintos tamaños y luego hacer algo parecido a esto:
<code cpp>
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, texImage0);
glTexImage2D (GL_TEXTURE_2D, 1, GL_RGB, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, texImage1);
glTexImage2D (GL_TEXTURE_2D, 2, GL_RGB, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, texImage2);
glTexImage2D (GL_TEXTURE_2D, 3, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, texImage3);
</code>
Afortunadamente, para hacer uso de los mipmaps OpenGL nos permite usar la función de la librería glut gluBuild2Dmipmaps, con solo una llamada a esta conseguimos lo que antes en varias a glTexImage2d, los parámetros son los mismos que para esta pero sin necesidad de indicar nivel ni borde.
Otra forma de hacer que se generen automáticamente los mipmaps es llamar a la siguiente función:
<code cpp>glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);</code>
Esta indica a OpenGL que cuando se defina una textura, se definan también los mipmaps.
=== Eliminar texturas ===
glDeleteTextures(GLsizei n, GLuint *textures) se le indican n texturas a eliminar a partir de textures.
==== Multitexturas ====
=== Verificación de soporte de multitexturas ===
Para poder saber si nuestra tarjeta soporta las multitexturas, el parámetro GL_ARB_multitexture, ha de estar en la lista GL_EXTENSIONS.
Para saberlo lo que haremos será meter en una variable char* la lista GL_EXTENSIONS mediante la función glGetString:
<code cpp>Lista = (char*) glGetString (GL_EXTENSIONS);</code>
La siguiente función buscará “searchStr” dentro de “str”:
<code cpp>
bool InStr (char *searchStr, char *str) {
char *extension;
char *endOfStr;
int idx=0;
endOfStr =str + strlen(str);
while (str < endOfStr) {
idx=strcspn(str,“ ”);
if 1)
return true;
str +=(idx + 1);
}
return false;
}
</code>
Si devuelve TRUE es que realmente soporta las multitexturas. Para verificar esto haríamos:
<code cpp>if (InStr (“GL_ARB_multitexture”, Lista)) { …</code>
Podemos mostrar cuantas multitexturas soporta nuestra tarjeta en un messagebox de la siguiente forma, si muestra 1 es que no las soporta:
<code cpp>
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &VarInt);
sprintf(VarCharDe30, “Multitexturas soportadas: %d”,VarInt);
MessageBox(hWnd, VarCharDe30, “Información”, NULL);
</code>
=== Activar el uso de las multitexturas ===
Para usar las multitexturas necesitaremos incluir el archivo “glext.h” que es donde se encuentran todas las extensiones de opengl.
Declararemos las siguientes variables:
<code cpp>
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL;
</code>
La primera es para ver las coordenadas de las multitexturas, la segunda representa a la unidad de multitextura, y la tercera es un puntero de comandos de multitextura.
=== Definir las texturas ===
Definiremos las texturas individualmente, como lo hemos hecho hasta ahora con las texturas simples:
<code cpp>
glGenTextures(2, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, Img[0].imageWidth, Img[0].imageHeight, GL_RGB, GL_UNSIGNED_BYTE, Img[0].imageData);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, Img[1].imageWidth, Img[1].imageHeight, GL_RGB, GL_UNSIGNED_BYTE, Img[1].imageData);
</code>
=== Indicar la multitextura ===
<code cpp>
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[1]);
</code>
Con glActiveTextureARB activamos una textura de las que tomarán parte en la “multitexturación”, esta tendrá el nombre GL_TEXTUREx_ARB. No nos olvidemos luego de activar GL_TEXTURE_2D y luego, indicaremos qué textura será la que usaremos.
=== Coordenadas de una multitextura ===
Las coordenadas de una multitextura sigue la misma norma que las demás, sólo cambia en que debemos pasarselas por la función glMultiTexCoord2fARB, con los parámetros de la 1ª textura, la x y la y, luego la siguiente textura de la misma forma:
<code cpp>
glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 0.0f, 1.0f);
glMultiTexCoord2fARB (GL_TEXTURE0_ARB, 0.0f, 1.0f);
glVertex2f(-4.0, 4.0);
</code>
=== Más sobre las texturas ===
Cuando usamos multitexturas y texturas simples, las texturas que compongan la multitextura las tendremos que definir en orden inverso:
<code cpp>
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[0]);
</code>
Además tendremos que indicar que vamos a usarlas (también en orden inverso) antes de dar sus coordenadas:
<code cpp>
glBindTexture(GL_TEXTURE_2D, texture[1]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS);
glMultiTexCoord2fARB (GL_TEXTURE1_ARB, 0.0f, 1.0f);
glMultiTexCoord2fARB (GL_TEXTURE0_ARB, 0.0f, 1.0f);
glVertex2f(-4.0, 4.0);
</code>
=== Texturas esféricas ===
Para indicar que una textura ha de expandirse desde el centro a los lados usaremos la función glTexGenf, que controla la forma en la que se crean las coordenadas de textura, a la cual han de pasarse los siguientes parámetros:
* A qué coordenada (GL_S (corresponde a la altura), GL_T (corresponde a la anchura), GL_R o GL_Q)
* GL_TEXTURE_GEN_MODE
* Parámetros: GL_OBJECT_LINEAR, GL_EYE_LINEAR, o GL_SPHERE_MAP (para esféricas).
Y luego activar las coordenadas que hayamos usado. Con glEnable(GL_TEXTURE_GEN_x); donde x puede ser S, T, R o Q.
<code cpp>
glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
</code>
Si usamos texturas de otro tipo no olvidemos desactivar (con glDisable) estas coordenadas.
=== Simular la multitextura ===
Para hacer una simulación de la multitextura, lo que debemos hacer es mostrar el objeto con una textura sin suavizado, luego mostar el objeto sobre el anterior con una textura… transparente:
<code cpp>
glBindTexture(GL_TEXTURE_2D, texture[1]);
DibuCub();
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
glDepthFunc(GL_EQUAL);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
glBindTexture(GL_TEXTURE_2D, texture[2]);
DibuCub();
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glDisable(GL_BLEND);
</code>
* glDepthMask Para activar (GL_TRUE) o desactivar (GL_FALSE) la escritura en el buffer z.
* glDepthFunc Selecciona la función de verificación para caras ocultas. Sus parámetros: GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GLEQUAL, GL_ALWAYS.
* glBlendFunc Selecciona el orígen y destino para la mezcla de colores. Sus parámetros son el como quedaría el color de detrás y el de delante. Para ello tenemos las siguientes indicaciones: GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, y GL_SRC_ALPHA_SATURATE.
Para repetir una textura en un mismo polígono… la clave está en las coordenadas:
<code cpp>
glTexCoord2f(0.0f, 0.0f); glVertex3f(poX-tam,poY+tam,poZ-tam);
glTexCoord2f(3.0f, 0.0f); glVertex3f(poX+tam,poY+tam,poZ-tam);
glTexCoord2f(3.0f, 3.0f); glVertex3f(poX+tam,poY-tam,poZ-tam);
glTexCoord2f(0.0f, 3.0f); glVertex3f(poX-tam,poY-tam,poZ-tam);
</code>
===== Efectos =====
==== Transparencias ====
Podemos hacer que una figura sea transparente manipulando su valor Alpha (ya sea en su color propio o en su textura). Para ello ha de estar activado el blending y cambiados sus valores por defecto.
<code cpp>
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
</code>
Ahora podremos dibujar un cuadrado como siempre lo hemos hecho, pero con otros valores de color:
<code cpp>
glColor4f(1.0f,0.0f,1.0f,0.6f);
glRectf (0.7f, 0.7f, 0.5f, 0.5f);
</code>
Para que las transparencias surjan efecto GL_DEPTH_TEST y la iluminación, han de estar desactivadas.
=== La ecuación de Blending ===
Para configurar el blending existen varias rutinas, entre ellas:
* glBlendFunc, a la que se le pasan dos parámetros que representan la funciones de blending. GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE
* glBlendEquation sólo se le pasa un parámetro, correspondiente a la ecuación de blending.
* glBlendFuncSeparate como glBlendFunc, pero esta última al indicar las funciones estas se aplican a los valores RGBA por igual, con glBlendFuncSeparate se indican por separado.
* glBlendColor, para indicar el color que OpenGL tomará por valor de alpha (por defecto el 0000).
=== Alpha Testing ===
Utilizado para decir a OpenGL qué alpha será efectiva y cual no según el color, para que su uso sea efectivo deberemos activar GL_ALPHA_TEST (cuidado, a veces dará problemas con el blending). Y utilizaremos la función glAlphaFunc a la cual se le pasa el modo en que actuará y un valor entre 0.0 y 1.0. El modo puede ser:
* GL_NEVER, el color nunca será transparaente.
* GL_ALWAYS, el color siempre será transparente.
* GL_LESS, el color será transparente si es menor al valor dado.
* GL_LEQUAL, el color será transparente si es menor o igual al valor dado.
* GL_EQUAL, el color será transparente si es igual al valor dado.
* GL_GEQUAL, el color será transparente si es mayor o igual al valor dado.
* GL_GREATER, el color será transparente si es mayor al valor dado.
* GL_NOTEQUAL, el color será transparente si no es igual al valor dado.
==== Niebla ====
Para conseguir niebla en OpenGL debemos activarla con glEnable(GL_FOG);. Con la niebla conseguimos que se distorsione el color de fondo. Utilizamos las funciones: glFogf, glFogfv, glFogi y glFogiv. Con los posibles parámetros situientes:
* GL_FOG_MODE → GL_LINEAR, GL_EXP, y GL_EXP2.
* GL_FOG_DENSITY → Un float
* GL_FOG_START y GL_FOG_END → Comienzo y fin de esta.
* GL_FOG_INDEX
* GL_FOG_COLOR → float niebla[4]={0.5,0.5,0.5,0.5};
<code cpp>
glEnable(GL_FOG);
glFogi(GL_FOG_MODE,GL_EXP);
glFogf(GL_FOG_DENSITY,0.2);
glFogfv(GL_FOG_COLOR, niebla);
</code>
===== Cuádricas =====
Dentro de glu.h encontramos una serie de “primitivas 3d”, estas en vez de ser polígonos son figuras prediseñadas. Para usarlas primero tendremos que crear una variable puntero cuadrica (puntero a variable del tipo GLUquadricObj):
<code cpp>GLUquadricObj *obj;</code>
Luego tendremos que indicarle que será una cuadrica (llamada a la función gluNewQuadric):
<code cpp>obj=gluNewQuadric();</code>
* Las cuádricas se muestran con un estilo concreto, para cambiar este utilizamos: gluQuadricDrawStyle (obj, estilo);. Siendo los estilos:
* GLU_FILL → Sólida.
* GLU_LINE → Líneas.
* GLU_POINT → Puntos.
* GLU_SILHOUETTE → Perfil.
* Las normales se generarán automáticamente si llamamos a la función: gluQuadricNormals (obj, tipo_normales);. El tipo de las normales viene dado por las siguientes flags:
* GLU_NONE → Sin normales.
* GLU_FLAT → Normales para cada polígono.
* GLU_SMOOTH → Normales para cada vértice.
* Para controlar la dirección de las normales usaremos: gluQuadricOrientation(obj, orientación);. La orientación será:
* GLU_OUTSIDE → Hacia fuera
* GLU_INSIDE → Hacia dentro (esto en las esferas si estamos dentro o fuera)
* Las coordenadas de textura con: gluQuadricTexture (obj, booleano);. El booleano indica si se generarán coordenadas o no, con GL_TRUE se generarán automáticamente, GL_FALSE indica que no. Para generar texturas la figura deberá de ser sólida y estar activadas las texturas.
==== Tipos de cuádricas ====
* Cilindros y conos: Al dibujar un cilindro podremos dibujar un cono si el radio superior (o inferior) de este es 0. Los cilindros no están tapados, para ello usaremos los discos. La función para dibujar un cilindro es gluCylinder con los parámetros:
* Radio de la base.
* Radio superior.
* Altura.
* Numero de caras (20 está bien).
* Número de pilas (“pisos”, como mínimo que sea 1).
* Discos: Con gluDisk y con los parámetros siguientes crearemos un disco o circunferencia:
* Radio Interior → El agujero del centro
* Radio Esterior → Tamaño del disco
* Segmentos → Como el número de caras en el cono
* Vueltas → El número de anillos internos
* Discos parciales: Es el dibujo de un disco como si fuese un arco… dibujando sólo un trozo, lo haremos con la función gluPartialDisk, los parámetros son los mismos que para gluDisk más ángulo inicial (donde empieza a dibujarse) y ángulo final (donde acaba de dibujarse).
* Esferas: Una esfera la dibujaremos con gluSphere y con los parámetros:
* Rádio
* Segmentos → El número de segmentos en total que tendrá.
* Pila → “Pisos” de la esfera.
<code cpp>
GLUquadricObj *obj = gluNewQuadric();
gluQuadricDrawStyle (obj, GLU_FILL);
gluQuadricOrientation(obj, GLU_OUTSIDE);
gluQuadricTexture(obj, GL_TRUE);
glBindTexture(GL_TEXTURE_2D, *i);
gluSphere(obj, 0.1, 25, 25);
</code>
===== Buffers =====
==== Stencil Buffer ====
Podemos usar los buffers para diferentes asuntos, como por ejemplo enfocar lo contrario a la escena que estamos mirando, pero el buffer más curioso es el buffer de estarcido (o Stencil Buffer), con él podemos indicar que el dibujo de una escena se haga sobre una figura. El código sería este:
<code cpp>
glClear( GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glPushMatrix();
gluLookAt(0.0,-3.0,-4.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
ang+=0.1;
glRotatef(ang, 0.0, 0.0, 5.0);
glDisable(GL_DEPTH_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, 1);
Agua();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPushMatrix();
glScalef(1.0, 1.2, 0.3);
glTranslatef(0.0,0.0,CalcPos());
glColor4f(1.0,1.0,1.0,0.1);
Pelota();
glPopMatrix();
glDisable(GL_STENCIL_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
Agua();
glColor4f(1.0,1.0,1.0, 1.0);
glTranslatef(0.0,0.0,-CalcPos());
Pelota();
glPopMatrix();
</code>
- Primero limpiamos todos los buffers, y colocamos la cámara. No nos olvidemos de indicar en la configuración de OpenGL los bits para este buffer: pfd.cStencilBits = 16;
- Para usarlo debemos deshabilitar el buffer de profundidad.
- glStencilOp es lo que se ejecutará si el buffer de estarcido falla (GL_KEEP (mantiene el valor actual), GL_ZERO (pone el buffer a 0), GL_REPLACE (se ejecutará glStencilFunc), GL_INCR, GL_DECR, GL_INVERT.
- glStencilFunc es la máscara cn la que se usará el buffer.
- glColorMask'' impide que se hagan cambios en los colores reflejados.glDisable(GL_DEPTH_TEST); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glEnable(GL_STENCIL_TEST); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); glStencilFunc(GL_ALWAYS, 1, 1); Agua();
Define el tamaño del buffer de estarcido.
glShadeModel (GL_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_ALPHA_TEST);