sábado, 28 de abril de 2012


Unidad IV

Como agregar un sombreado a un polígono en OpenGL

Es el método que utiliza OpenGL para rellenar de color los polígonos. Se especifica con la función glShadeModel. Si el parámetro es GL_FLAT, ogl rellenará los polígonos con el color activo en el momento que se definió el último parámetro; si es GL_SMOOTH, ogl rellenará el polígono interpolando los colores activos en la definición de cada vértice.

Este código es un ejemplo de GL_FLAT:

glShadeModel(GL_FLAT);

glBegin(GL_TRIANGLES);  

  glColor3f(1.0f, 0.0f, 0.0f);  // activamos el color rojo

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

glColor3f(0.0f, 1.0f, 0.0f);  // activamos el color verde

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

 glColor3f(0.0f, 0.0f, 1.0f);  // activamos el color azul

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

glEnd();



Técnicas de sombreado clásicas y avanzadas

Clásicas: Iluminación local.

Luces que no son extensas, como las reales, sino inextensas, puntuales. Y, por añadidura, se relacionan con los objetos como mónadas aisladas, sin tener en cuenta la interacción entre ellos. Esto explica lo artificioso de muchas de las técnicas que se describirán más adelante y que buscan compensar estas limitaciones.

Cálculos de iluminación por vértices

Para aplicar iluminación a un objeto necesitamos asociar un vector normal a cada vértice del objeto. Cuando tenemos la normal calculada tenemos que normalizarla, o sea, dividir ese vector por su propio modulo para que sea unitario, pero también podemos hacer que se encargue la OpengGl activando la normalización con el comando glEnable GL_NORMALIZE o desactivarla con glDisable GL_NORMALIZE.

El usar GL_NORMALIZE dependerá de nuestra aplicación ya que si forzamos a que sea OpenGl el que las utilice se ralentiza por que le estamos obligando a hacer mas cálculos de los que debe.

Para definir las normales en opengl utilizaremos la función glNormal3f(X,Y,Z) por ejemplo para definir una cara con 4 vértices la definiremos de la siguiente manera:

GlBegin GL_QUADS

glNormal3f nX,nY,nZ

glvertex3f x,y,z

glvertex3f x,y,z

glvertex3f x,y,z

glvertex3f x,y,z

glEnd

Renderizado en Tiempo real

La idea fundamental del procesado en tiempo real es que todos los objetos deben ser descompuestos en polígonos. Estos polígonos serán descompuestos a su vez en triángulos. Cada triángulo será proyectado sobre la ventana bidimensional y rellenado con los colores adecuados para reflejar los efectos de la iluminación, texturas, etc. Una vez se han generado los triángulos, en la pipeline existen dos partes claramente diferenciadas: una primera etapa operaciones realizadas sobre cada uno de los vértices, y después de que éstos se proyecten sobre la ventana, entonces comienza una segunda fase de cálculos realizados para cada pixel cubierto por triángulos.

Realistas: iluminación global

Son aquellos en los que se considera que la intensidad de luz en un punto de la superficie de un objeto se debe a las fuentes luminosas y al resto de los elementos existentes en la escena.

Realistas: Iluminación global

Son sencillos y rápidos pero proporcionan imágenes muy simples, que no representan adecuadamente el modo en que la luz ilumina los objetos y los espacios. Esto no quiere decir que no sean útiles para un gran número de casos, y es muy importante calibrar adecuadamente que es lo que se necesita, pues puede muy bien ocurrir que un cálculo local proporcione imágenes relativamente esquemáticas pero más adecuadas para la representación de un proyecto.





Trazado de rayos

El trazado de rayos computa la interacción de la luz desde un punto de vista determinado y es particularmente adecuado para superficies reflectantes. Puede utilizarse como propiedad especifica de un determinado material. Se traza un rayo desde la posición del observador a través de cada uno de los píxeles del plano de proyección (una de las ventajas del raytracing es que los rayos que se procesan son sólo los rayos que parten del observador ),

Radiosidad

Está basado en principios generales que se pueden encontrar en un manual general sobre rendering. En el estadio inicial la escena consta de dos tipos de objetos: objetos que emiten luz y objetos que reciben luz. A partir de aquí, en una primera vuelta, se computa la luz que recibe cada objeto o, en una aproximación más exacta, cada parte de un objeto, según una subdivisión cuya densidad puede precisarse en sucesivas aproximaciones.

Cálculos de iluminación por pixel

La iluminación por píxel en tiempo real es una tecnología revolucionaria ofrecida como primicia por NVIDIA Shading Rasterizer. La iluminación dinámica a nivel de píxel libera a los desarrolladores de las restricciones de otros sistemas de iluminación y pone a su alcance toda una gama de sofisticados efectos. Antes de que el color final del píxel sea decidido, un cálculo de iluminación debe ser computado para sombrear a los píxeles basados en alguna luz que puede estar presente en la escena.

Alto Acabado

Sombreado Constante o plano. Un cálculo para todo el polígono. Obtenemos una intensidad que aplicamos a un conjunto de puntos de un objeto (p.ej. todo un triángulo). Aceleramos el proceso de síntesis. Correcto si se verifica: Fuente de luz en el infinito. Observador en el infinito. El polígono representa una superficie plana real del objeto que se modela y no es una aproximación de un objeto curvo.

Sombreado Constante o Plano

Obtenemos una intensidad que aplicamos a un conjunto de puntos de un objeto

*Aceleramos el proceso de síntesis

*Correcto si se verifica.

* Fuente de luz en el infinito

*Observador en el infinito

Modelo de Reflexión Phong

El modelo de reflexión de Phong es eficiente y suficientemente aproximado a la realidad física para producir buenas imágenes, bajo una variedad de condiciones de luz y propiedades de materiales. Apoya los tres tipos de interacciones material-luz: ambiente, difusa y especular. Si se tiene un conjunto de fuentes puntos, con componentes independientes para cada uno de los tres colores primarios para cada uno de los tres tipos de interacciones material-luz.

Ray Tracing

En muchas formas, ray tracing es una extensión al enfoque de rendering con un modelo de iluminación local. Está basado en la observación previa que, de los rayos de luz saliendo de una fuente, los únicos que contribuyen a la imagen son aquellos que entran al lente de la cámara sintética y pasan por el centro de proyección.

Buffer Stencil.

Stencill Buffer es una memoria intermedia que analiza y actualiza píxeles (con sus operaciones) junto con “depth buffer” o buffer de profundidad. Añade planos de bits adicionales para cada píxel además de los bits de color y profundidad.



Stencil buffer es similar al buffer de profundidad en que los dos son colección de planos de bit que no se pueden mostrar. Del mismo modo que el buffer de profundidad asocia a cada píxel de la ventana un valor de profundidad, el stencil buffer asocia su propio valor a cada píxel mostrado. Cuando el buffer de profundidad esta activado los valores de profundidad son usados para aceptar o rechazar fragmentos, del mismo modo los valores de Stencil buffer son usados para aceptar o rechazar fragmentos.

Buffer de Acumulación

Normalmente se usa un buffer de acumulación para unir las 2 imágenes.

Fuentes de Luz

La luz puede dejar una superficie mediante dos procesos fundamentales:

            Emisión propia

            Reflexión

Normalmente se piensa en una fuente de luz como un objeto que emite luz solo mediante fuentes de energía internas, sin embargo, una fuente de luz, como un foco, puede reflejar alguna luz incidente a esta del ambiente.

Fuentes de Luz

La luz puede dejar una superficie mediante dos procesos fundamentales:

* Emisión propia

* Reflexión

Luz Ambiente

La luz ambiente ilumina por igual todas las zonas en sombra para simular el efecto de interacción entre objetos que hace que las partes en sombra de los objetos queden parcialmente iluminadas.

Spotlights (direccionales)

Los spotlights se caracterizan por un rango delgado de ángulos por los cuales se emite luz. Se puede construir un spotlight sencillo de una fuente de punto limitando los ángulos de donde la luz de la fuente se puede ver. Se puede usar un cono cuyo ápice está en ps, apuntando en la dirección ls, y cuyo ancho está determinado por el ángulo θ.

martes, 17 de abril de 2012


¿Como se grafica un circulo y una elipse?

Debido a que la elipse y la circunferencia son figuras simétricas el algoritmo genera los vértices del primer cuadrante solamente. Los vértices del segundo cuadrante se crean aplicando simetría axial respecto a los vértices del primer cuadrante, los vértices del tercer cuadrante con respecto a los del segundo y los vértices del cuarto cuadrante con respecto a los del tercero.

Las ecuaciones utilizadas son las paramétricas polares


Como la circunferencia es un caso particular de la elipse (cuando radioX = radioY) a partir de este momento hablaremos solamente de la elipse.

Este algoritmo consiste en dividir la elipse en cuatro partes iguales y a cada una agregarle la cantidad de vértices (pasos) que se indiquen. El punto de partida es crear cuatro vértices los cuales están situado en los ejes del plano XY a la distancia del radio correspondiente al eje, partiendo siempre del origen de coordenadas, por lo que la elipse siempre se crea en el origen de coordenadas y en el plano XY. Si se desea desplazar del origen de coordenadas entonces hay que aplicarle una transformación a cada vértice[1].

En la figura 1 se muestra una elipse con cero pasos.


Fig 1. El cuadrado amarillo formado por los cuatro vértices es la elipse formada cuando la cantidad de pasos es cero. La elipse rosada es otra que tiene el mismo radio pero usando un método adaptativo, esta se muestra como referencia solamente.

Si la cantidad de pasos es uno entonces se divide 90º / 2 y este va a ser el ángulo del vértice agregado en el primer cuadrante. Una vez obtenido el ángulo se evalúan las ecuaciones para ese ángulo y la posición obtenida es la del vértice nuevo. En el resto de los cuadrantes se calcula la posición del vértice a través de simetría para evitar los cálculos. Este caso puede observarse en la figura 2.

Fig 2. El polígono amarillo es el obtenido cuando la cantidad de pasos es uno. La elipse rosada tiene el mismo radio pero usando un método adaptativo, esta se muestra como referencia solamente.

Pseudocódigo

Entradas

float radioX
float radioY
byte s // Cantidad de pasos

Salidas

La lista de vértices y las coordenadas de textura de cada vértice

A continuación aparece el pseudocódigo utilizado por el algoritmo:

Crear una lista de vértices (vacía)
Agregar a la lista el vértice para el ángulo cero que está situado en la coordenada (radioX, 0, 0)
Si la cantidad de pasos es cero Entonces
Agregar los tres vértices restantes (ver fig 1):
o    (0, radioY, 0)
o    (-radioX, 0, 0)
o    (0, -radioY, 0)
Si no Entonces
Crear una constante
para el incremento del ángulo de rotación donde s es la cantidad de pasos.
Crear una variable aa que indica el valor del ángulo de rotación actual
Mientras aa sea menor que 90º
Calcular la posición del nuevo vértice usando como ángulo aa
Agregar el nuevo vértice a la lista de vértices
Fin mientras
// En este momento ya se han calculado todos los vértices del primer cuadrante.
Agregar el vértice que está situado en el eje Y positivo (0, radioY, 0)
Agregar los vértices del segundo cuadrante utilizando simetría axial.
Agregar el vértice que está situado en el eje X negativo (-radioX, 0, 0)
Agregar los vértices del tercer cuadrante utilizando simetría axial.
Agregar el vértice que está situado en el eje Y negativo (0, -radioY, 0)
Agregar los vértices del tercer cuadrante utilizando simetría axial.
Fin si

Generar las coordenadas de textura (ver código fuente de ejemplo)

Nota: La cantidad de pasos oscila en el rango 0..255, pero en la práctica con 100 es suficiente. GMax y 3DS MAX usan 6 pasos por defecto.

lunes, 16 de abril de 2012


gluLookAt

¿Qué hace?

Lo que realiza la funcion gluLookAt es una roto-traslacion,  especifica en donde se situara la camara, hacia donde mirara esta y cual va a ser el orden de los ejes de coordenadas.

Esta sentencia tiene nueve argumentos que describen tres puntos, los valores de X0, Y0, Z0, representa el punto hacia donde mira la cámara virtual, este punto normalmente se identificaen el origen de coordenadas (0,0,0), ahora bien podemos definir nosotros el punto que más propicio sea para nuestra escena.

Los siguientes tres argumentos representan el punto donde se situara la camra de visualizacion, estas coordenadas no deben coincidir con el punto al cual miramos.

Las últimas tres coordenadas representa el vector de vista hacía arriba, es decir indica cual será el vector cuya dirección será hacia arriba, apuntará hacia la parte superior del monitor.

Carro

#include <GL/glut.h>
float posicion;
int grados;
void reshape(int width, int height){
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //gluPerspective(60.0f,1.0f, 0.01f, 100.0f);
    glOrtho(-10, 10, -10, 10, 0.1f, 20);
  glTranslatef(-3.5f, -2.0f, -15.0f);
  //glRotatef(40, 1.0f, 1.0f, 0.0f);
  glMatrixMode(GL_MODELVIEW);
}

void dibujar_tornillos(){
  glPushMatrix();
   glColor3f(0.0f,0.0f,0.0f); 
   glTranslatef(0.0f,0.0f, 1.0f);
   glPushMatrix();
    glTranslatef(-0.5f,-0.1f, 0.0f);
    glutSolidSphere(0.1f,20,20);
   glPopMatrix();
   glPushMatrix();
    glTranslatef(0.5f,-0.1f, 0.0f);
    glutSolidSphere(0.1f,20,20);
   glPopMatrix();
   glPushMatrix();
    glTranslatef(0.0f,0.5f, 0.0f);
    glutSolidSphere(0.1f,20,20);
   glPopMatrix();
   glPushMatrix();
    glTranslatef(-0.2f,-0.5f, 0.0f);
    glutSolidSphere(0.1f,20,20);
   glPopMatrix();
   glPushMatrix();
    glTranslatef(0.2f,-0.5f, 0.0f);
    glutSolidSphere(0.1f,20,20);
   glPopMatrix();
  glPopMatrix();
}
void dibujar_rueda_y_tornillos_delante(){
  glPushMatrix();
  glColor3f(1.0f,0.0f,0.5f); 
  glTranslatef(1.5f,0.0f, 0.0f);
  glRotatef(grados, 0.0f, 0.0f, 1.0f);
  glutSolidSphere(1.0f,20,20);
  dibujar_tornillos();
  glPopMatrix();
}
void dibujar_rueda_y_tornillos_detras(){
  glPushMatrix();
  glColor3f(1.0f,0.0f,0.5f); 
  glTranslatef(5.5f,0.0f, 3.0f);
  glRotatef(grados, 0.0f, 0.0f, 1.0f);
  glutSolidSphere(1.0f,20,20);
  dibujar_tornillos();
  glPopMatrix();
}

void dibujar_ventanas(){
  glPushMatrix();
  glColor3f(0.0f,0.0f,0.5f); 
  glTranslatef(1.2f,2.0f, 0.01f);
  glBegin(GL_TRIANGLES);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(0.8f, 0.0f, 0.0f);
      glVertex3f(0.8f, 0.8f, 0.0f);
  glEnd();
  glBegin(GL_QUADS);
      glVertex3f(0.8f, 0.0f, 0.0f);
      glVertex3f(1.7f, 0.0f, 0.0f);
      glVertex3f(1.7f, 0.8f, 0.0f);
      glVertex3f(0.8f, 0.8f, 0.0f);
  glEnd();
  glTranslatef(1.9f,0.0f, 0.0f);
  glBegin(GL_QUADS);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(0.9f, 0.0f, 0.0f);
      glVertex3f(0.9f, 0.8f, 0.0f);
      glVertex3f(0.0f, 0.8f, 0.0f);
  glEnd();
  glBegin(GL_TRIANGLES);
      glVertex3f(0.9f, 0.0f, 0.0f);
      glVertex3f(1.7f, 0.0f, 0.0f);
      glVertex3f(0.9f, 0.8f, 0.0f);
  glEnd();
  glPopMatrix();
}
void techo(){
  glPushMatrix();
  glTranslatef(1.0f,2.0f, 0.0f);
  glBegin(GL_TRIANGLES);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(1.0f, 0.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, 0.0f);
  glEnd();
  glBegin(GL_QUADS);
      glVertex3f(1.0f, 0.0f, 0.0f);
      glVertex3f(3.0f, 0.0f, 0.0f);
      glVertex3f(3.0f, 1.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, 0.0f);
  glEnd();
  glBegin(GL_TRIANGLES);
      glVertex3f(3.0f, 0.0f, 0.0f);
      glVertex3f(4.0f, 0.0f, 0.0f);
      glVertex3f(3.0f, 1.0f, 0.0f);
  glEnd();
  glPopMatrix();
}
dibujar_cuerpo_coche(){
  glColor3f(0.0,1.0,0.0); 
  glTranslatef(posicion,0.0f, 0.0f);
  glBegin(GL_QUADS);
      glVertex3f(0.0f, 0.0f, 0.0f);
      glVertex3f(7.0f, 0.0f, 0.0f);
      glVertex3f(7.0f, 2.0f, 0.0f);
      glVertex3f    (0.0f, 2.0f, 0.0f);
  glEnd();   
}
void display(){
  GLfloat angulo;
  int i;
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  dibujar_cuerpo_coche();
 
  techo();
  dibujar_ventanas();
  dibujar_rueda_y_tornillos_delante();
  dibujar_rueda_y_tornillos_detras();

  glutSwapBuffers();
}
void init(){
  glClearColor(0,0,0,0);
  posicion=0;
  grados=0;
  glEnable(GL_DEPTH_TEST);
}

void keyboard(unsigned char key, int x, int y){
  switch(key)
    {
    case 'p':
    case 'P':
      posicion+=0.1f;
      grados-=180/3.14;
      display();
      break;
  
    case 'o':
    case 'O':
      posicion-=0.1f;
      grados+=180/3.14;
      display();
      break;
    case 27:   // escape
//      exit(0);
      break;
    }
}
int main(int argc, char **argv){
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowPosition(50, 50);
  glutInitWindowSize(500, 500);
  glutCreateWindow("Carro");
  init();
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
  glutMainLoop();
  return 0;
}