practica4_animacion
Transcripción
practica4_animacion
PRACTICA # 4 DOBLE BÚFER Y ANIMACIÓN ÁLVAREZ FAJARDO GERARDO ADOLFO GRUPO 4 Cuestionario previo 1. Investigue y tienen. Esto es, a. frame b. depth c. stenci reporte cuáles búferes se implementan en OpenGL y que función investigue el buffer o búfer de color, tanto frontal como posterior. buffer o búfer de profundidad. buffer. Para poder trabajar con OpenGL, primero se debe crear un contexto de trabajo, este contexto contiene el estado actual de maquina finita, asi como las referencias a los diferentes buffers de trabajo, estos buffers se pueden ver como zonas de memoria correspondiendo a la pantalla en las cuales OpenGL va a dibujar, en general a parte del buffer de color (GL_COLOR_BUFFER) que es el buffer en el cual se van a dibujar las primitivas, existen otro tipo de buffers mas especializados. La configuracion en memoria de estos buffers (cuantos bits representan un pixel, etc) depende de la manera como fue creado el contexto OpenGL y de las limitaciones del hardware, por esto no se puede acceder directamente sino solo a traves de las primitivas OpenGL. OpenGL puede funcionar adicionalmente de dos maneras, de modo directo o indirecto: Modo directo: las primitivas se van dibujando a medida que se van definiendo. Instruccion -> Buffer de Color = Pantalla Modo indirecto: las primitivas se guardan en una lista y solo se dibujan cuando el usuario decida o la lista este llena, esto permite optimizar la fase de dibujo. Instruccion-> Pila de instrucciones-> flush -> Buffer de Color = Pantalla En este modo cuando se desea que OpenGL pinte lo que esta en la lista se utiliza la instrucción glFlush(): esta instruccion obliga a pintar y no espera a que el hardawre termine para continuar con el programa, analogamente la glFinish() obliga a pintar pero espera a que el hw termine antes de continuar con el programa. En el modo indirecto, OpenGL permite definir dos buffers de colores (doublebuffer), asi un buffer corresponde a lo que se ve en pantalla y otro a el buffer donde se esta pintando, de esta manera una vez que se ha pintado todo lo deseado y se quiere que esto aparezca en pantalla se intercambian los buffers, esta instruccion depende del sistema operativo para esto se utilizara la instruccion de la libreria portable glut: glutSwapBuffers() (esta ejecuta implicitamente glFlush o glFinish), en este modo glFlush y glFinish obligan a pintar en el buffer de dibujo pero esto NO sera visible hasta intercambiar buffers. Para utilizar los diferentes tipos de buffers de OpenGL estos se deben crear al crear el contexto opengl, en el caso de la utilizacion de la libreria glut, al llamar la funcion: glutInitDisplayMode (buffers), buffers es una combinacion de valores indicando que buffers se deben crear i.e. para crear un contexto con doble buffer rgba y z-buffer buffers seria = GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH, los valores para crear otro tipos de buffers son: GLUT_STENCIL = buffer de stencil. GLUT_ACCUM = buffer de acumulacion. Todos los buffers deben ser activados o desactivados dependiendo de la utilizacion con el comando glEnable(param) o glDisable(param), donde param corresponde al tipo de test que se activa, i.e param = GL_STENCIL_TEST para el buffer de stencil, o param=GL_DEPTH_TEST para el buffer de profundidad (activado por defecto). Los buffers de stencil, acumulacion y profundidad funcionan aplicando una serie de operaciones y funciones sobre los valores que se van a escribir en el buffer, asi para manejar la funcion del buffer de profundidad se utiliza la funcion glDepthFunc(funcion), donde funcion indica cuando pasa el test de profundidad (GL_EQUAL, GL_LESS, GL_LEQUAL (por defecto), GL_NOTEQUAL, GL_GREATER, GL_GEQUAL). En el caso de la funcion de test, se define glStencilFunc(funcion, mascara), donde función son las mismas que para la profundidad, y mascara indica que bits del stencil se utilizan. Adicionalmente a la funcion de stencil, se utiliza la operacion de stencil glStencilOp(fallo,zfallo,zpaso) que especifica como se modifica el buffer de stencil dependiendo de si la funcion del buffer stencil y z fallaron o pasaron, pueden ser GL_KEEP,GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT. 2. Investigue y reporte cómo se hace una secuencia animada de caricaturas. Cualquier forma de animación, bien sea esta al estilo antiguo de Disney (frames hechos a mano), cine, animaciones gif, videos de computadora y videojuegos utiliza una técnica que consiste en mostrar rápidamente, a entre 30 y 60 cuadros por segundo (fps) un conjunto de imágenes que varían de forma ligera, para crear la sensación de movimiento aprovechando una característica de la visión humana llamada persistencia de la visión. En el caso particular de los videojuegos, los frames se hacen mezclando un conjunto de figuras bidimensionales (sprites) o haciendo complejos cálculos sobre la manera en que se vería una proyección bidimensional de una escena “abstracta” tridimensional (3d), o una mezcla de ambas, y realizando en cada caso un cálculo del pequeño movimiento que debe hacer cada objeto para dar una sensación fluida de animación. El búfer doble Se ha mencionado ya que para poder dar la sensación de movimiento es necesario mostrar cerca de 30 cuadros por segundo. El problema es que el computador no genera “instantáneamente” la imagen a partir de los sprites o la escena 3d, por lo que si se posee una sola área de dibujo en la memoria se verán molestos intervalos entre un frame y otro (mientras se hacen los cálculos y se realizan movimientos de memoria). Es en este sentido que la estrategia de múltiples buffers, dos en este caso funciona. La estrategia es bastante simple, se tienen 2 áreas de memoria, una es la que actualmente se muestra en pantalla (Front Buffer) y la otra es sobre la cual se realizan los cálculos (back buffer). Cuando la escena se termina de dibujar se intercambian los papeles entre los buffers, es decir, el Back se convierte en Front y viceversa. De esta manera el usuario no ve cuando se está renderizando sobre el buffer, solo ve el resultado final, con la ventaja adicional de que el proceso es bastante eficiente, ya que no se realizan copias entre áreas de memoria, al menos para mostrar los frames. La parte teórica funciona de manera similar en diferentes entornos, bien sea DirectX, OpenGL o SDL. En el caso práctico de OpenGL, la especificación del API no incluye una garantía de que los dos buffers estén presentes (obviamente al menos el Front debe existir), por lo que las funciones para solicitar y manejar mas de un buffer dependen del entorno en el que se esté ejecutando la aplicación (Windows, Mac, Linux, etc.). Microsoft provee una implementación propia del Doble Buffer para Windows y esta misma funcionalidad es ofrecida por Glut y similares para diferentes sistemas operativos de manera genérica. Implementación Glut Es bastante sencilla (ver tutorial Hola Mundo 3d en OpenGL), basta solicitar el modo Doble Buffer al inicializar la ventana y cada vez que se terminan de pasar los parámetros de dibujo a OpenGL y estos han sido ejecutados, pedir un cambio de Buffer así: 1. 2. 3. 4. 5. 6. inicializarVentana() { … glutInitDisplayMode(GLUT_DOUBLE| …) … } 7. 8. render() 9. { 10. //comandos de dibujo 11. glutSwapBuffers(); 12. } La función glutInitDisplayMode() se encarga de solicitar al sistema el modo requerido por el programador, en este caso Doble Buffer (GLUT_DOUBLE) y otros que se le suministren seguidos de “|” (GLUT_DOUBLE | GLUT_RGB por ejemplo). glutSwapBuffers() intercambia el Buffer actual sobre el que se está dibujando (Back) con el que se muestra en pantalla (Front). Implementación Windows La implementación del doble buffer es algo mas compleja (ver tutorial Hola Complicado Mundo OpenGL – Win32), pero básicamente está ligada al PIXELFORMATDESCRIPTOR – HDC con los cuales se solicita y al HDC, con el cual se intercambian los Buffers. El código luce así: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. HDC hdc=NULL; … crearVentana() { //todo el código para crear la ventana static PIXELFORMATDESCRIPTOR pfd= { ... ,... |PFD_DOUBLEBUFFER | //tercer parámetro ,... }; PixelFormat=ChoosePixelFormat(hdc,&pfd); } render() { //El código de render SwapBuffers(hdc); } Donde el pdf es la variable para inicializar el modo de pantalla y hdc es el hardware device context asociado a la ventana. 3. Investigue y reporte que hace la función glutDisplayFunc() void glutDisplayFunc (void (*func)(void)): el glutDisplayFunc fija el servicio repetido de la exhibición para la ventana actual. 4. Investigue y reporte que hace la función glutIdleFunc(). void glutIdleFunc (void (*func)(void)) el glutIdleFunc fija el servicio repetido ocioso global. GLUT llama a ésta función todo el tiempo, haciendo que se logre una animación Sintaxis: void glutIdleFunc(void (*func)(void)); 5. Investigue y reporte que hace la función glutMainLoop(); La función glutMainLoop() es el bucle infinito de gestión de eventos. Se entra en ella pero jamás se sale. Cuando se dispara el evento que provoca la salida del bucle, el programa acaba. Cualquier cosa que pongamos detrás de la llamada a glutMainLoop() no se ejecutará jamás. Con la función glutMainLoop(), avisamos a GLUT que estamos listos para entrar en loop de eventos de la aplicación. La función glutMainLoop() hace que el computador espere la ocurrencia de un evento para entonces invocar a la función respectiva (callback). Ejercicio 1 Uno de los cubos solo se mantiene rotando sobre su eje y el otro además de rotar, se traslada. Ejercicio 2. Animando la cámara. Esta vez la cámara se mantiene rotando 360° mientras que la animación en ambos cubos continúa. Ejercicio 3. Animación libre. 1) Un par de esferas. Una se mueva de derecha a izquierda y la otra de izquierda a derecha, ambas sobre el eje x. 2) Un par de esferas. Una se mueve de arriba hacia abajo y la otra de abajo hacia arriba, en el eje y. Conclusiones Con esta práctica aprendí a animar objetos; Ahora ya creando y teniendo cualquier objeto en la pantalla, puedo darle movimiento y rotación en distintos sentidos. Comprendí la utilización de los búfers.