ManuelRomero/grafica/ej1

De WikiEducator
Saltar a: navegación, buscar
Road Works.svg Trabajo en proceso, espera cambios frecuentes. Tu ayuda y retroalimentación son bienvenidos.
Ver página de charlas.
Road Works.svg




Practica 2

Enunciado
Se trata de realizar de forma diferente el mismo dibujo
Vamos a dibujar un disco o agujero de tres formas diferentes
Obtenemos la misma imagen, pero de la forma real nos permite modificar facilmente su aspecto
  • Observamos el escenario

OpenGEPractica2.png

Crear un nuevo proyecto

  • Creamos un nuevo proyecto según hemos visto en el capítulo anterior
Archivo ==> Nuevo ==> Proyecto 
Seleccionamos  Proyecto vacío
  • Ponemos nombre al proyecto
  • Seleccionamos la opción crear directorio para la solución

Creamos el proyecto

  • Ahora creamos un nuevo archivo fuente para nuestro proyecto
  • Para ello abrimos la venta Explorador de soluciones
  • Esta ventana muestra la estructura de directorios creada para nuestra solución o proyecto

VCPPNuevoArchivo3.png

  • En él seleccionamos la carpeta de Archivo de código fuente y con el botón derecho creamos un nuevo fichero

VCPPNuevoArchivo4.png

  • Pegamos el fuente que se nos ha facilitado en la práctica.
///////////////////////////////////////////////////////////////////////////////////////////        
// circularAnnuluses.cpp
//
// This program draws three identical-looking circular annuluses in three different ways - 
// see comments below.
//
// Interaction:
// Press the space bar to toggle between wirefrime and filled for the lower annulus.
// 
// Sumanta Guha.
/////////////////////////////////////////////////////////////////////////////////////////// 
 
#include <cstdlib>
#include <cmath>
#include <iostream>
 
#ifdef __APPLE__
#  include <GLUT/glut.h>
#else
#  include <GL/glut.h>
#endif
 
#define PI 3.14159265
#define N 40.0 // Number of vertices on the boundary of the disc.
 
using namespace std;
 
// Globals.
static int isWire = 0; // Is wireframe?
static long font = (long)GLUT_BITMAP_8_BY_13; // Font selection.
 
// Routine to draw a bitmap character string.
void writeBitmapString(void *font, char *string)
{  
   char *c;
 
   for (c = string; *c != '\0'; c++) glutBitmapCharacter(font, *c);
} 
 
// Function to draw a disc with center at (X, Y, Z), radius R, parallel to the
// xy-plane.
void drawDisc(float R, float X, float Y, float Z)
{
   float t;
   int i;
 
   glBegin(GL_TRIANGLE_FAN);
      glVertex3f( X, Y, Z);
      for(i = 0; i <= N; ++i)
	  {
         t = 2 * PI * i / N; 
         glVertex3f(X + cos(t) * R, Y + sin(t) * R, Z);
	  }
   glEnd();
}
 
// Drawing routine.
void drawScene(void)
{  
   float angle;
   int i;
 
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the buffers including 
                                                       // the depth buffer.
 
   glPolygonMode(GL_FRONT, GL_FILL);
 
   // Upper left circular annulus: the white disc overwrites the red disc.
   glColor3f(1.0, 0.0, 0.0);
   drawDisc(20.0, 25.0, 75.0, 0.0);
   glColor3f(1.0, 1.0, 1.0);
   drawDisc(10.0, 25.0, 75.0, 0.0);
 
   // Upper right circular annulus: the white disc is in front of the red disc blocking it.
   glEnable(GL_DEPTH_TEST); // Enable depth testing. 
   glColor3f(1.0, 0.0, 0.0);
   drawDisc(20.0, 75.0, 75.0, 0.0);
   glColor3f(1.0, 1.0, 1.0);
   drawDisc(10.0, 75.0, 75.0, 0.5); // Compare this z-value with that of the red disc.
   glDisable(GL_DEPTH_TEST); // Disable depth testing.
 
   // Lower circular annulus: with a true hole.
   if (isWire) glPolygonMode(GL_FRONT, GL_LINE);else glPolygonMode(GL_FRONT, GL_FILL);
   glColor3f(1.0, 0.0, 0.0);
   glBegin(GL_TRIANGLE_STRIP);
      for(i = 0; i <= N; ++i)
	  {
         angle = 2 * PI * i / N; 
         glVertex3f(50 + cos(angle) * 10.0, 30 + sin(angle) * 10.0, 0.0);
         glVertex3f(50 + cos(angle) * 20.0, 30 + sin(angle) * 20.0, 0.0);
	  }
   glEnd();
 
   // Write labels.
   glColor3f(0.0, 0.0, 0.0);
   glRasterPos3f(15.0, 51.0, 0.0);
   writeBitmapString((void*)font, "Overwritten");
   glRasterPos3f(69.0, 51.0, 0.0);
   writeBitmapString((void*)font, "Floating");
   glRasterPos3f(38.0, 6.0, 0.0);
   writeBitmapString((void*)font, "The real deal!");
 
   glFlush();
}
 
// Initialization routine.
void setup(void) 
{
   glClearColor(1.0, 1.0, 1.0, 0.0);  
}
 
// OpenGL window reshape routine.
void resize(int w, int h)
{
   glViewport(0, 0, (GLsizei)w, (GLsizei)h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}
 
// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
   switch(key) 
   {
      case ' ':
         if (isWire == 0) isWire = 1;
         else isWire = 0;
         glutPostRedisplay();
         break;	  
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}
 
// Routine to output interaction instructions to the C++ window.
void printInteraction(void)
{
   cout << "Interaction:" << endl;
   cout << "Press the space bar to toggle between wirefrime and filled for the lower annulus." << endl;  
}
 
// Main routine.
int main(int argc, char **argv) 
{
   printInteraction();
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); // Initialize the buffers 
                                                             // including the depth buffer.
   glutInitWindowSize(500, 500);
   glutInitWindowPosition(100, 100); 
   glutCreateWindow("circularAnnuluses.cpp");
   setup(); 
   glutDisplayFunc(drawScene); 
   glutReshapeFunc(resize);  
   glutKeyboardFunc(keyInput);
   glutMainLoop(); 
 
   return 0;  
}

Explicación del fuente

  • A continuación se va a explicar el código que genera el escenario anterior
  • Por ser esta práctica la primera se explicará de forma detallada
  • de esta forma servirá para empezar a entender cómo usar OpenGL en programación

Cabecera

  • definimos bibliotecas stardar de C++
#include <cstdlib>
#include <cmath>
#include <iostream>
  • Se incluye la biblioteca para usar glut
  • Todas las funciones que empiecen por glut serán de esta biblioteca
#ifdef __APPLE__
#  include <GLUT/glut.h>
#else
#  include <GL/glut.h>
#endif
  • Constantes
  1. PI para operaciones matemáticas
  2. N el número de vértices para el límite de un disco
#define PI 3.14159265
#define N 40.0 // Number of vertices on the boundary of the disc.
  • Se define un namespace para el proyecto
using namespace std;
  • Variables globales que son estáticas y por lo tanto inicializadas
// Globals.
static int isWire = 0; // Is wireframe?
static long font = (long)GLUT_BITMAP_8_BY_13; // Font selection.

Programa principal

  • El programa principal de nuestra aplicación es el siguientes
int main(int argc, char **argv) 
{
   //Mesaje para el usuario de cómo usar el programa
   printInteraction();
 
   //Rutinas de inicialización
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); // Initialize the buffers 
                                                             // including the depth buffer.
   glutInitWindowSize(500, 500);
   glutInitWindowPosition(100, 100); 
   glutCreateWindow("circularAnnuluses.cpp Practica 1");
 
   //Dibuja nuestro escenario
   setup(); 
   glutDisplayFunc(drawScene); 
   glutReshapeFunc(resize);  
   glutKeyboardFunc(keyInput);
   glutMainLoop(); 
 
   return 0;  
}

Rutinas de inicialización de OpenGL y GLUT

Ver referencia web en página oficial

  • Los primeros métodos de la librería glut se invocan al principio
  • Sirven para inicalizar la librería.
void glutInit(int *argcp, char **argv);
Inicializa la librería GLUT para que pueda usar una sesión con el sistema de ventanas. En caso de que no se pueda retorna un error
argcp Es un puntero a la variable argc del main (ver int main(char* argv, int argc); Sirve para poder pasar de la línea de comandos valores para GLUT
argcv Igual que en el caso anterior, pero ahora a la variable argv del main().
  • Toma los valores que le pasamos al main y se los envía a la bibliteca de GLUT
void glutInitDisplayMode(unsigned int mode);
Establece el modo de visualización incial.
  • Este valor por defecto es RGBA (colores básicos y el canal Alpha)
  • Se pueden concatenar bits para especificar el modo de visualización. ver la siguiente lista de valores posibles
  • En nuestro caso el método es invocado glutInitDisplayLaod(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
  1. GLUT_SINGLE establece un single buffered window.
  2. GLUT_RGB es como GLUT_RGBA establece este mode de visualización
  3. GLUT_DEPTH establece buffer de profundidad (para que objetos de adelante oculten a los de atrás, sin que éstos desaparezcan de la escena)
http://www.opengl.org/documentation/specs/glut/spec3/node12.html#SECTION00033000000000000000
void glutInitWindowSize(int width, int height);
void glutInitWindowPosition(int x, int y);
  • Establecen el tamaño y la posición inicial de la ventana cuando es creada con el método glutCreateWindow(...)
int glutCreateWindow(char *name);
  • Crea la ventana de nivel superior de nuestra aplicación.
  • el parámetro name será el título de la ventana
*El int que retorna servirá para identificar la ventana
  • Este valor se puede usar al invocar al método glutSetWindow.
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); // Initialize the buffers 
                                                             // including the depth buffer.
   glutInitWindowSize(500, 500);
   glutInitWindowPosition(100, 100); 
   glutCreateWindow("circularAnnuluses.cpp Practica 1");

Nuestro código

  • Una vez inicializado el entorno vienen nuestra acciones
  • El el programa principal son las siguientes invocaciones
 printInteraction();
 ...
 setup(); 
 glutDisplayFunc(drawScene); 
 glutReshapeFunc(resize);  
 glutKeyboardFunc(keyInput);
 glutMainLoop(); 
 return 0;
void printInteraction(void)
  • Se imprime un mensaje por la pantalla para interactuar con el usuario.
  • Este contenido va a la consola no a la ventana windows
  • es código c++ (objetos de la clase iostream)
  • Por ello hay que hacer el include de la clase iostream
  • El código de esta parte quedaría
...
#include <iostream>
...
void printInteraction(void)
{
   cout << "Interaction:" << endl;
   //cout << "Press the space bar to toggle between wirefrime and filled for the lower annulus." << endl;  
   cout << "Presiona la barra espaciadora para alternar entre el anillo relleno de color y relleno de alambre ." << endl;  
}
void glutDisplayFunc(void (*func)(void));
  • glutDisplayFunc Carga la funcion pasada como parámetro en la ventana de window actual
void glutReshapeFunc(void (*func)(int width, int height));
  • glutReshapeFunc vuelve a dibujar la ventana de window con la función que recibe como parámetro
void glutKeyboardFunc(void (*func)(unsigned char key,int x, int y));
  • glutKeyboardFunc Asigna el callback del teclado a la ventana actual de windows,
  • cuando presionemos una tecla la función pueda recoger el valor y reaccionar ante el evento
void glutMainLoop(void);

glutMainLoop hace que glut entre en su blucle de procesamiento

  • Esta función no retorna una vez invocada
  • Sí que se invocarán a las fucniones que previamente han sido registradas .

El contenido de nuestro código

glClearColor
  • Es una función propia de OpenGl como todas las que empiezan por gl
  • La función glClearColor(float red, float green, flota green, float alpha)
  • Especifica el color que pasamos como argumentos con el que se borrara el buffer al hacer un glClear().
  • Los parámetros son valores reales entre 0 y 1
  • Los tres primeros espefician el color
  • El cuarto es el nivel de transparencia canal alpha.
// Rutinas de inicialización
void setup(void) 
{
   glClearColor(1.0, 1.0, 1.0, 0.0);  
}
Ahora es cuando vamos a hacer funcionar nuestro código
glColor3f(float red, float green, float blue);
  • Estableciendo un color, previamente borramos el que hubiera
void glPolygonMode( GLenum face,GLenum mode);
glPolygonMode(GL_FRONT, GL_FILL);
  • especifica el modo de rasterización de los polígonos,
  • el parámetro face En este caso GL_FILL hace que todo el área del polígono se rellene.
  • Otras opciones GL_POINT o GL_LINE
Dibujando el círculo o disco

Discos sobredibujados

drawDisc(float radio ,float x, float y, float z)
  • Dibuja un disco con un radio con un espesor y orientación , y una posición (x,y,z)
  • Esta sección dibujaría dos circulos uno negro y pequeño (10 de radio)y otro rojo y grando (20 de radio)
  • Ambos con el mismo centro (25,75,0)
  • Observar la coordenada z a 0 hace que estén a la misma profundidad, por lo que el orden en el que se dibuja es importante (primero el grande y luego el pequeño)
  • La consecuencia es que el pequeño se superpone al grande
   glColor3f(1.0, 0.0, 0.0);
   drawDisc(20.0, 25.0, 75.0, 0.0);
   glColor3f(1.0, 1.0, 1.0);
   drawDisc(10.0, 25.0, 75.0, 0.0);

Usando el Z-buffer o test de profundidad

  • En este caso con la componente z hacemos que uno esté por delante del otro
  • Peviamente habilitamos el test de profundidad para que un objeto que esté por delante de otro lo oculte
  • En este caso da lo mismo el orden en el que dibujemos los discos
// Upper right circular annulus: the white disc is in front of the red disc blocking it.
   glEnable(GL_DEPTH_TEST); //Habilitamos el test de profundidad. 
   glColor3f(1.0, 0.0, 0.0);
   drawDisc(20.0, 75.0, 75.0, 0.0);
   glColor3f(1.0, 1.0, 1.0);
   drawDisc(10.0, 75.0, 75.0, 0.5); //Observar la componente z.
   glDisable(GL_DEPTH_TEST); // Deshabilitamos el test de profundidad.

Dibujando realmente un disco

  • En esta caso vamos a dibujar una serie de vértices que vamos a contectar entre ellos
  • Así generaremos la forma deseada
  • isWire es un booleano que actualizamos al tocar la tecla de espacio
  1. true se visualiza en el polígono formado sólo líneas GL_LINES
  2. fasle se visualiza el polígono formado relleno GL_FILL
// Lower circular annulus: with a true hole.
  if (isWire) glPolygonMode(GL_FRONT, GL_LINE);else glPolygonMode(GL_FRONT, GL_FILL);
  glColor3f(1.0, 0.0, 0.0);
  glBegin(GL_TRIANGLE_STRIP);
     for(i = 0; i <= N; ++i)

{

        angle = 2 * PI * i / N; 
        glVertex3f(50 + cos(angle) * 10.0, 30 + sin(angle) * 10.0, 0.0);
        glVertex3f(50 + cos(angle) * 20.0, 30 + sin(angle) * 20.0, 0.0);

}

  glEnd();
Dibujando el circulo sobreescrito
  • En este caso primero dibujamos un
Test de profundidad

El algoritmo del Z-buffer es del tipo espacio-imagen. Cada vez que se va a renderizar un pixel, comprueba que no se haya dibujado antes en esa posición un pixel que esté más cerca respecto a la cámara. Este algoritmo funciona bien para cualquier tipo de objetos: cóncavos, convexos, abiertos y cerrados. Para activarlo, hay que hacer una llamada a glEnable(GL_DEPTH_TEST) Esta llamada le dice a OpenGL que active el test de profundidad. Además