Índice de Contenidos :. APIs para Gráficos 3D :: 1 OpenGL :: 1.1 Direct X / Direct 3D :: 1.2 Nvidia Cg :: 1.3 Algunas Técnicas Habituales :: 2 Algoritmos de Interiores :: 2.1 Algoritmos de Exteriores :: 2.2 Sistemas de Partículas :: 2.3 Shading :: 2.4 Simulaciones Físicas :: 3 Open Dynamics Engine (ODE) :: 3.1 Ejemplo en Blender Game Engine :: 3.2 «Todo debe hacerse tan simple como sea posible, pero no más». Albert Einstein Sesión 6 :: Transp. 2
Qué es una API para Gráficos 3D? :. API: Application Programming Interface. Es una capa software situada entre el programador y la tarjeta aceleradora, presentando una abstracción consistente al programador. Algunas APIs famosas: Glide: Diseñada específicamente para tarjetas 3DFx. Su primera versión salió en 1994. Muy similar a OpenGL, pero específica para esta familia de tarjetas. Actualmente está siendo poco utilizada. OpenGL. Direct3D (parte de DirectX). Sesión 6 :: Transp. 3
OpenGL :. Creado en 1990 por Silicon Graphics, y actualmente mantenido por un comité de compañías (Nvidia, Microsoft, SGI...). API Multiplataforma, simple y elegante. API de Modo Inmediato: Las llamadas afectan directamente al frame buffer. Esto simplifica el desarrollo; permite independencia de estructuras de datos. Independiente de la gestión de ventanas, bloques de memoria... Ampliamente utilizado en el desarrollo de videojuegos. glclearcolor(0.0, 0.0, 0.0, 0.0); glclear(gl_color_buffer_bit ); gllookat(0, 0, -10, 0, 0, 0, 0, 1, 0); glbegin(gl_triangles); glcolor3f(1,0,0); glvertex3f(-1,0,0); glcolor3f(1,1,0); glvertex3f(1,0,0); glcolor3f(0,0,1); glvertex3f(0,1,0); glend(); Gran comunidad de usuarios. Sesión 6 :: Transp. 4
Direct3D :. Primera versión creada para W95. No llegó a ser funcional hasta la versión 5. Gran esfuerzo para convencer a los desarrolladores. DirectX no es sólo de representación; módulos para manejo de sonido, teclado... Actualmente DirectX es el API dominante en juegos para PC y en Xbox. Direct3D funcionalmente equivalente a OpenGL. Inicialización de un dispositivo Direct3D LPDIRECT3D8 g_pd3d = NULL; LPDIRECT3DDEVICE8 g_pd3ddevice = NULL; HWND hwnd; D3DDISPLAYMODE d3ddm; if(failed(g_pd3d ->GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &d3ddm))) return false; D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp)); d3dpp.windowed = TRUE; d3dpp.swapeffect = D3DSWAPEFFECT_DISCARD; d3dpp.backbufferformat = d3ddm.format; if(failed(g_pd3d->createdevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_ VERTEXPROCESSING, &d3dpp, &g_pd3ddevice))) return false; Sesión 6 :: Transp. 5
NVidia CG :. CG significa "C para Gráficos" y es un lenguaje de alto nivel, multiplataforma. El compilador de Cg genera código ASM para ser introducido en los proyectos. El lenguaje del programador es similar a C. MultiAPI: OpenGL y DirectX. Requiere soporte mínimo DirectX8 y OpenGL 1.4 Multiplataforma: Win. y Linux. Multihardware: NVidia, ATI, Matrox... Asegura la optimización manteniendo la compatibilidad. Sesión 6 :: Transp. 6
NVidia CG :. vpconn main(appdata IN, uniform float4x4 WorldViewProj, uniform float3x3 WorldIT, uniform float3x4 World,uniform float3 LightVec, uniform float3 EyePos) { vpconn OUT; float3 worldnormal = normalize (mul(worldit, IN.Normal)); float4 temppos; temppos.xyz = IN.Position.xyz; temppos.w = 1.0; float3 worldspacepos = mul(world, temppos); float3 verttoeye = normalize(eyepos - worldspacepos); float3 halfangle = normalize(verttoeye + LightVec); OUT.TexCoord0.x = max(dot(lightvec, worldnormal), 0.0); OUT.HPosition = mul(worldviewproj, temppos); return OUT; } Fragmento (casi completo) de Anisotropic L. Sesión 6 :: Transp. 7
Algoritmos de Interiores :. Combinando clipping + culling, sólo nos quedamos con 1/8 de la geometría original. Sin embargo, sigue siendo demasiado. En Interior de Edificios, uso de Técnicas de Oclusión. Algoritmos Clipping Culling Oclusión LOD Interiores Sí Sí Sí Opcional Exteriores Sí Sí Opcional Sí Para conseguir una tasa de fps determinada, suelen utilizarse BSPs como estructuras de datos (Usado en Quake, Doom...) BSP: Binary Space Partitioning es un árbol binario que nos permite clasificar la geometría espacialmente. Independiente del punto de vista, podemos consultarlo en cualquier momento. Sesión 6 :: Transp. 8
BSP: Construcción del Árbol :. Algoritmo de generación del árbol recursivo. 1. Inicialmente, partir del conjunto de triángulos total. 2. Elegir el triángulo que mejor divide al conjunto por la mitad. 3. Calcular el plano que contiene al triángulo. 4. Almacenar el triángulo y el plano en el nodo actual. 5. Dividir los triángulos restantes según el lado del plano donde estén. 6. Si alguna sublista de triángulos del paso 5 no es vacía, crear un nuevo nodo y ejecutar de nuevo el punto 2. 1 2 3 Sesión 6 :: Transp. 9
BSP: Construcción del Árbol :. 4 5 6 Finalmente, cada nodo del BSP almacena exactamente un triángulo, que se utiliza para dividir el conjunto de datos en ese punto. Sesión 6 :: Transp. 10
BSP: Construcción del Árbol :. 7 Sesión 6 :: Transp. 11
BSP: Y todo esto para qué? :. Ordenación Dependiente de la Vista. 1. Comenzar con el punto de vista y el nodo raíz del árbol. 2. Mirar si está en frente o dentrás del plano. 3. Si estás en frente 3.1. Mirar el nodo "back". 3.2. Dibujar el triángulo. 3.3. Mirar el nodo "front". 4. Sino 4.1. Mirar el nodo "front". 4.2. Dibujar el triángulo. 4.3. Mirar el nodo "back". Según el árbol de la Trp. anterior, los triángulos se pintarían (de atrásadelante): 6, 5.1.1, 1.1, 5.1.2, 9, 8, 1.2, 2, 3, 4, 5.2 Sesión 6 :: Transp. 12
Leafy-BSP: Detección de Oclusiones :. Modificación de BSP para detectar oclusiones: Leafy-BSP. Constituyen el núcleo del pipeline de Quake. La geometría se propaga a las hojas. En las hojas guardamos la lista de triángulos de cada nodo. Esto nos constituyen "celdas". Si "lanzamos rayos" desde cada celda a el resto y vemos cuales alcanzamos directamente (visibles), podemos saber cuales están ocultas desde esa celda. Matriz PVS (Potentially Visible Set); es una matriz de booleanos (bits) que indica si la celda de la fila X es visible por la celda de la columna Y. Evidentemente es simétrica. Sesión 6 :: Transp. 13
Leafy-BSP: Detección de Oclusiones :. Sesión 6 :: Transp. 14
Algoritmos de Exteriores :. Uno de los principales problemas (a parte del LOD progresivo) es almacenar las mallas de los escenarios. Alternativas: Mapas de Altura: Imágenes en escalas de grises que codifican la altura del terreno. Quadtrees: Árboles cuaternarios que subdividen cada nodo en 4. Refinan cada nodo calculando su punto medio y los vértices adyacentes con valores (xmin, zmin), (xmin, zmax), (xmax, zmin) y (xmax, zmax). z Nivel N+1 Nivel N División Nodo Original Xmin Zmax Xmax Zmax Xmin Zmin Xmax Zmin x Sesión 6 :: Transp. 15
Sistemas de Partículas :. Existen elementos que no pueden representarse de forma cómoda como un conjunto de triángulos (un fuego, ondas, un árbol,...). Sistemas de partículas: descripciones matemáticas de sistemas dinámicos y complejos. Características: Dinámicos y dependientes del tiempo. Altamente paralelos con componentes individuales. Complejos. Sistemas Locales: No hay interacción con otras partículas. Sistemas Globales: El comportamiento de una partícula se ve influenciada por ciertas propiedades de otras. Estructuras de datos en Partículas. Depende de lo que queramos representar: Posición, velocidad, tamaño, color, transparencia, forma, tiempo de vida... Sesión 6 :: Transp. 16
Shading :. Implementación del modo de interactuar la luz con los materiales de los objetos (iluminación y sombreado). Un modelo muy sencillo: La luz se calcula como suma de los componentes ambiental, difusa y especular. Ambiente: Iluminación base de la escena. Proviene de todas direcciones. Difusa: Luz reflejada por las superficies en todas direcciones. Especular: Luz reflejada en la "dirección del espejo". Evidentemente, la intensidad es dependiente del punto de vista. Modelos más avanzados: Trazado de Rayos. Radiosidad. Muy utilizado por sus resultados y su eficiencia. BDRF (Bidirectional Reflactance Distribution Function). Sesión 6 :: Transp. 17
Simulaciones Físicas :. 2 Modelo (Mediante Ecuaciones) 1 Proceso Físico (El sistema a Simular) 3 Algoritmo de Simulación (Método de resolución de las ecuaciones) 5 Simulación (Ejecución del Programa) 4 Programa (Implementación real del algoritmo) SDK Sesión 6 :: Transp. 18
Aplicaciones del SDK :. Dinámicas de Cuerpos Sólidos Cuerpos Sólidos Articulaciones Contactos y Colisiones Fricción Muelles... Uso en Videojuegos Realización de un mundo virtual. Todo tipo de juegos con actores que interactúan. Simular elementos. Uso en applicaciones de Tiempo Real À Controlador (Hw. Real) Simulador Uso en Videojuegos À Simulador Ä Usuario Sesión 6 :: Transp. 19
Cómo funciona el SDK :. Unión1 Cuerpo1 Unión2 Cuerpo2 Cuerpo3 Descripción del Sistema Mecánico Estado actual del sistema SDK Fuerzas que actúan sobre el sistema Siguiente Estado del sistema (Después del Incremento de Tiempo) Incremento de Tiempo (Paso) Sesión 6 :: Transp. 20
Introducción a ODE :. ODE (Open Dynamics Engine) es una librería libre (licencias GPL y BSD), multiplataforma de calidad industrial para la simulación de dinámicas en cuerpos rígidos. Funciona muy bien con estructuras articuladas. Pensada para ser utilizada en simulaciones en tiempo real (videojuegos). Implementa un integrador muy estable. Asegura contactos duros. Un objeto no penetra dentro de otro. Implementa su propio sistema de detección de colisiones. Sesión 6 :: Transp. 21
Cuerpos Rígidos :. Propiedades interesantes para simular (varían a lo largo del tiempo): Posición (x,y,z). Calculada sobre su centro de masa. Velocidad Lineal (vx, vy, vz). Orientación (matriz de rotación 3x3). Velocidad angular (wx, wy, wz). Propiedades habitualmente constantes: Masa del cuerpo. Posición del centro de masas. Matriz de Inercia. Sesión 6 :: Transp. 22
El "Main" Típico de Simulación Física :. 1. Creación del mundo. 2. Creación de los cuerpos. 3. Estado de los cuerpos en el mundo. Inicialización 4. Añadir articulaciones (restricciones). 5. Añadir la geometría de colisión de los objetos. 6. Crear un grupo de articulaciones para añadir las restricciones de colisión. 7. Bucle principal. 7.1. Aplicar las fuerzas a los cuerpos. 7.2. Ajustar los parámetros de las restricciones. 7.3. Llamar a la detección de colisiones. 7.4. Crear una restricción de colisión para cada Bucle punto de colisión. Principal 7.5. Añadir cada restricción al grupo. 7.6. Avanzar en el integrador un paso. 7.7. Eliminar las restricciones del grupo. Sesión 6 :: Transp. 23
«Hello World» con PyODE :. import ode Ver Ejemplo_0.py world = ode.world() # Creamos el objeto Mundo world.setgravity ( (0,-9.81,0) ) body = ode.body(world) # Creamos un objeto M = ode.mass() M.setSphere (2500.0, 0.05) M.mass = 1.0 body.setmass(m) body.setposition( (0,2,0) ) body.addforce ( (100,200,0) ) sim_time = 0.0 # Iniciamos la simulacion... dt = 0.04 while sim_time < 2.0: x,y,z = body.getposition() u,v,w = body.getlinearvel() print "%1.2fsec: pos=(%6.3f, %6.3f, %6.3f)" % (sim_time, x,y,z) world.step(dt) # Avanzamos un paso en el integrador simulation_time+=dt Sesión 6 :: Transp. 24
Articulaciones / Restricciones :. Cada paso que avanza el integrador, aplica las fuerzas correspondientes a las articulaciones definidas. Estas uniones deben establecerse en el sistema de coordenadas global. Unión Ball Unión Hinge Unión Hinge2 Gestión de articulaciones más eficiente definiendo agrupaciones. Unión Slider Unión Universal Sesión 6 :: Transp. 25
Manejo del Error :. Parámetro de Reducción del Error (ERP) Causas del Error: Acumulación en el integrador en diferentes pasos. Mal posicionamiento por parte del programador. Mezclado de Fuerzas de Restricción (CFM) No todas las restricciones son duras. Si CFM es 0, la restricción es totalmente dura. Si tiene un valor positivo, es flexible. ERP: Fuerza especial aplicada para devolver al cuerpo a su alineación correcta. Valor entre 0 (no se aplica corrección) y 1 (cálculo totalmente preciso). Recomendado entre 0.1 y 0.8. Sesión 6 :: Transp. 26
Un Ejemplo con Articulaciones :. import pygame from pygame.locals import * import ode Fragmento de Código del Fichero Ejemplo_1.py Ver Ejemplo_1.py # Creación de un objeto Mundo world = ode.world() world.setgravity((0,-9.81,0)) # Creación de los 2 cuerpos body1 = ode.body(world) M = ode.mass() M.setSphere(2500, 0.05) body1.setmass(m) body1.setposition((1,2,0)) #... Código del segundo objeto # Conecta Body1 con el entorno j1 = ode.balljoint(world) j1.attach(body1, ode.environment) j1.setanchor( (0,2,0) ) # Conectamos el obj2 con el obj1 j2 = ode.balljoint(world) j2.attach(body1, body2) j2.setanchor( (1,2,0) ) # bucle de simulacion fps = 50 dt = 1.0/fps while loopflag: x1,y1,z1 = body1.getposition() x2,y2,z2 = body2.getposition() pygame.draw.circle(...) pygame.draw.line(...) Sesión 6 :: Transp. 27
Manejo de las Colisiones :. Pasos del Manejo de Colisiones 1. Llamadas a las funciones de Preserva que el Cuerpo1 entre en el Cuerpo2. Genera una velocidad en la dirección del vector Normal. Tiempo de vida usual: 1 paso del integrador. detección de colisiones. "Qué está tocando qué". 2. Generación de articulaciones de contacto para cada punto de contacto. 3. Unión de las restricciones de contacto en un grupo. 4. Avance del integrador en 1 paso. 5. Eliminación de las articulaciones de contacto. Sesión 6 :: Transp. 28
Ejemplo de Colisiones Realistas 3D :. # Método "Main" world = ode.world() # Creamos objeto "Mundo" world.setgravity( (0,-9.81,0) ) world.seterp(0.8) world.setcfm(1e-3) Fragmento de Código del Fichero Ejemplo_2.py Ver Ejemplo_2.py space = ode.space() # Objeto "espacio" floor = ode.geomplane(space, (0,1,0), 0) bodies = [] # Lista de cuerpos # Grupo de restricciones contactgroup = ode.jointgroup() while running: [...] elif e.key==k_b: drop_object("box") [...] for b in bodies: draw_body(b) n = 2 for i in range(n): # Simulación # Detección de colisiones y creación # de articulaciones de contacto. space.collide((world,contactgroup), near_callback) # Un paso del mundo (integrador) world.step(dt/n) # Eliminación de las restr. colisión contactgroup.empty() Sesión 6 :: Transp. 29
Ejemplo de Colisiones Realistas 3D :.... Continuación def near_callback(args, geom1, geom2): # Comprueba si los objetos colisionan contacts = ode.collide(geom1, geom2) Fragmento de Código del Fichero Ejemplo_2.py Ver Ejemplo_2.py world,contactgroup = args # Crea las uniones de contacto for c in contacts: c.setbounce(0.2) c.setmu(5000) j = ode.contactjoint(world, contactgroup, c) j.attach(geom1.getbody(), geom2.getbody()) def drop_object(type): global bodies if type=="box": body = create_box(world, space, 1000, 1.0,0.2,0.2) [...] body.setposition( (random.gauss(0,0.1),3.0,random.gauss(0,0.1)) ) m = mat4().rotation(random.uniform(0,2*pi), (0,1,0)) body.setrotation(m.tolist()) bodies.append(body) Sesión 6 :: Transp. 30
Ejemplo de Colisiones Realistas 3D :.... Continuación Fragmento de Código del Fichero Ejemplo_2.py def create_box(world, space, density, lx, ly, lz): # Crea el cuerpo Ver Ejemplo_2.py body = ode.body(world) M = ode.mass() M.setBox(density, lx, ly, lz) def draw_body(body): body.setmass(m) x,y,z = body.getposition() R = body.getrotation() # Dispone los parámetros para T = mat4() # el objeto a dibujar T[0,0] = R[0] [...T[0,1]=R[1]] body.shape = "box" T[3] = (x,y,z,1.0) body.boxsize = (lx, ly, lz) # Crea una caja para la # detección de la colisión geom = ode.geombox(space, lengths=body.boxsize) geom.setbody(body) return body glpushmatrix() glmultmatrixd(t.tolist()) if body.shape=="box": sx,sy,sz = body.boxsize glscale(sx, sy, sz) glutsolidcube(1) [... if body.shape == "sphere"] glpopmatrix() Sesión 6 :: Transp. 31
Hagámoslo más realista... Fricción! :. Ejercicio: En ejemplo anterior, las esferas no detienen su movimiento debido a la falta de rozamiento. Simulemos una implementación básica de fricción, sabiendo que: El método getangularvel() nos devuelve una lista de 3 valores (las 3 componentes del vector de velocidad angular). El método setangularvel() permite especificar el vector de velocidad angular, también mediante una lista de 3 valores. Sesión 6 :: Transp. 32
Un ejemplo con Blender :. Paso01.blend En este ejemplo crearemos un entorno para realizar una sencilla simulación física utilizando el GameEngine. Con los elementos de la capa 2, crearemos un circuito Ver pasos/paso00.blend como el de la figura (Sig. Trp) Fichero de comienzo Duplicar un objeto: D En rotaciones, se puede avanzar de 5 en 5 grados si mantenemos pulsada la tecla. Con trabajamos en modo de precisión. Puede resultar interesante cambiar el centro geométrico del objeto para realizar rotaciones. Para ello, con el objeto seleccionado, situamos el puntero 3D en el nuevo centro y pinchamos en "Centre Cursor", de la ventana de edición. Sesión 6 :: Transp. 33
Un ejemplo con Blender :. Paso01.blend Sesión 6 :: Transp. 34
Un ejemplo con Blender :. Paso02.blend Añadimos a los dos objetos "generador" superiores la siguiente sección de bloques lógicos, para que generen un objeto "bola". En uno pondremos de sensor la tecla Space y en otra el Enter. La bola, situada en la capa 3 tendrá que tener propiedades de Actor, Dynamic y Rigid Body (para que ruede con la fricción del recorrido). Sesión 6 :: Transp. 35
Un ejemplo con Blender :. Paso03.blend Al generador inferior (donde deben eliminarse las bolas que lleguen), le añadimos una propiedad booleana llamada "final". Finalmente, volvemos al objeto "bola" (tercera capa), y añadimos un conjunto de bloques lógicos como se muestran a continuación. Ejercicio: Añadir más elementos de interacción en el circuito, (puertas, ascensores, muelles...) Sesión 6 :: Transp. 36