Upload
vantram
View
225
Download
0
Embed Size (px)
Citation preview
Aplicaciones 3Dcon WebGLJUAN CARLOS CONDE RAMÍREZ
COMPUTER ANIMATION
Un motor 3D de JavaScript, I
FCC-BUAP 2
“La necesidad es la madre de la invención.”
– Susanna Centlivre
•Existen bastantes buenas librerías de código abierto disponibles para el desarrollo deaplicaciones WebGL, entre las que se encuentran:
◦ GLGE (http://www.glge.org/)
◦ SceneJS (http://scenejs.org/)
◦ CubicVR (http://cubicvr.org/)
Un motor 3D de JavaScript, II
FCC-BUAP 3
•Cada librería hace las cosas un poco diferente, pero comparten el objetivo de implementarfunciones de alto nivel y amigables para el desarrollo con WebGL.
•La librería que se usará por ahora se llama Three.js, creación de Mr.doob, también conocidocomo Ricardo Cabello Miguel, un programador Web originario de Barcelona, España.
•Three.js proporciona un fácil e intuitivo conjunto funciones para la creación y manipulación deobjetos, usualmente 3D. Es poderoso, su código fuente está alojado en GitHub y es mantenidocontinuamente por varios autores. Por si fuera poco, es considerado como uno de los motoreslíder del área.
Un motor 3D de JavaScript, III
FCC-BUAP 4
•Posiblemente tú quieras desarrollar tu propio motor 3D para WebGL, pero antes de hacerlo,deberías echarle un vistazo al gran trabajo ya se ha hecho para WebGL.
•El hecho de que existan diversos toolkits como Three.js se debe, en gran parte, a la potencia delas máquinas virtuales (VM) de JavaScript dentro de los navegadores Web en los últimos años.
•A través del curso, harás uso extensivo de Three.js, pero por ahora vale la pena presentar unbreve resumen de lo que este motor tiene para ofrecer.
Alcance de Three.js , I
FCC-BUAP 5
•Three.js oculta los detalles de renderizado 3D◦ Abstrae los detalles del API de WebGL, representando tanto escenas 3D como mallas, materiales y luces
(i.e. los elementos típicos con los que trabajan los programadores).
•Three.js es orientado a objetos◦ Los programadores trabajan con verdaderos objetos JavaScript en lugar de sólo hacer llamadas a
funciones escritas en JavaScript.
•Three.js es rico en características y funciones◦ Se trata de algo más que sólo una envoltura para el API de WebGL, Three.js contiene muchos objetos
pre-construidos útiles para el desarrollo de juegos, animaciones, presentaciones, modelos en altaresolución y efectos especiales.
Alcance de Three.js , II
FCC-BUAP 6
•Three.js es de rápida ejecución◦ Emplea las mejores prácticas en desarrollo de gráficos 3D para mantener un alto rendimiento sin
sacrificar usabilidad.
•Three.js soporta interacción con el usuario◦ WebGL no proporciona soporte nativo para la “selección” de objetos. Three.js tiene un sólido soporte
para dicha selección, facilitando así la creación de aplicaciones interactivas.
•Three.js se ocupa de la matemática inherente◦ Tiene objetos poderosos y fáciles de usar para el manejo de la matemática 3D implícita, tales como
vectores, matrices y proyecciones.
Alcance de Three.js , III
FCC-BUAP 7
•Three.js soporta archivos en formatos predeterminados◦ Es posible cargar archivos en formatos de “texto” exportados por paquetes populares de modelado 3D;
incluso hay formatos binarios y formatos en JSON específicos para Three.js
•Three.js es extensible◦ Three.js es completamente fácil de personalizar, así como de agregar nuevas características. Si por
ejemplo no existe el tipo de dato que se necesita, éste puede ser creado, integrado y usado.
•Three.js también funciona con el canvas 2D de HTML5◦ También puede renderizar contenido en un canvas 2D, en caso de que el canvas 3D no sea compatible
con el navegador, permitiendo así que la excepción sea manejada elegantemente por otra solución.
Alcance de Three.js , IV
FCC-BUAP 8
•Sin embargo, también es importante mencionar algunas de las cosas que NO hace Three.js
◦ No es un motor de juegos o una plataforma de mundo virtual. Carece de algunas de las funciones máscomunes de esos sistemas como barras publicitarias, avatares y física.
◦ Tampoco tiene el soporte de red integrado que se podría esperar si se estuviera programado un juegomultijugador.
•Si se requieren dichas funcionalidades, estas se tendrían que desarrollar sobre Three.js. Aún así,su poder y simplicidad hacen de Three.js una excelente herramienta para comenzar el viaje conWebGL.
Configuración de Three.js , I
FCC-BUAP 9
•El primer paso es obtener el último paquete de Three.js, para esto se tienen dos opciones:
1. Obtenerlo desde la rama “master” en GitHub, ya sea clonando el directorio o descargándolo en formade Zip en: https://github.com/mrdoob/three.js/tree/master
2. Descargando el paquete comprimido desde el sitio Web oficial ubicado en: https://threejs.org/
•Una vez con el paquete descargado, se puede usar la versión minimalista localizada enbuild/Three.js. Pero también se puede utilizar la versión completa ubicada en la carpeta src.
•La documentación del API disponible en el directorio de GIT es muy básica, por lo que serecomienda usar la que está disponible en el sitio Web.
Configuración de Three.js , II
FCC-BUAP 10
•Three.js está construido con el Compilador Closure de Google (Google Closure Compiler); elarchivo principal contiene la librería completa de Three.js construida a partir de varios archivosfuente separados.
◦ Se puede visitar https://developers.google.com/closure/compiler, si no se está familiarizado con Closurey se requiere saber más al respecto.
◦ Si no se quiere lidiar con este concepto, se puede tratar a Three.js como una caja negra por ahora.
•Se recomienda dedicar un tiempo para revisar la documentación introductoria y a familiarizarsecon Three.js, o al menos se sugiere probar los ejemplos disponibles en la carpeta examples.
Una página simple, I
FCC-BUAP 11
•El objetivo de este ejercicio es demostrar lo simple que es poner en marcha Three.js.
•Por lo tanto a continuación se muestra el código completo que muestra una nueva versión delprograma que dibuja un cuadrado (mostrado anteriormente).
•La diferencia entre este ejemplo y el anterior es que se usan sólo 30 líneas en lugar de 150,además del uso de un conjunto de objetos en lugar de un conjunto de buffers. A continuaciónEjemplo2-1. Una página simple usando Three.js.
Una página simple, II
FCC-BUAP 12
<!DOCTYPE html>
<html>
<head>
<title>A Simple Three.js Page</title>
<script src="../build/three.js"></script>
<script>
function onLoad()
{
// Se obtiene el elemento div con el id “container”
var container = document.getElementById("container");
// Se crea el objeto renderer de Three.js, para el renderizado de la escena
var renderer = new THREE.WebGLRenderer();
// Se construye el objeto renderer del mismo tamaño del contenedor
renderer.setSize(container.offsetWidth, container.offsetHeight);
// Se agrega el objeto renderer al contenedor como un elemento DOM hijo
container.appendChild( renderer.domElement );
Una página simple, III
FCC-BUAP 13
// Se crea una nueva escena Three.js
var scene = new THREE.Scene();
// Se crea una cámara y se agrega esta a la escena
var camera = new THREE.PerspectiveCamera( 45,
container.offsetWidth / container.offsetHeight, 1, 4000 );
camera.position.set( 0, 0, 3.3333 );
scene.add( camera );
// Ahora, se crea un rectánculo y se agrega éste a la escena
var geometry = new THREE.PlaneGeometry(1, 1);
var mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( ) );
scene.add( mesh );
// Se renderiza la escena percibida a través de la cámara
renderer.render( scene, camera );
}
</script>
</head>
Una página simple, IV
FCC-BUAP 14
<body onLoad="onLoad();">
<div id="container“ style="width:500px; height:500px; background-color:#000000">
</div>
</body>
</html>
Una página simple, V
FCC-BUAP 15
Una página simple, VI
FCC-BUAP 16
•Observaciones del código de ejemplo:
1. Se usa una etiqueta <script> exclusivamente para incluir la librería three.js
2. El programa completo que dibuja el cuadrado está contenido en una misma función de nombre onLoad(), que es invocada por el evento onLoad de la página Web.
3. scene es un objeto de alto nivel en la jerarquía de gráficos de Three.js, contiene a los otros objetos gráficos.
4. A la escena se le tienen que agregar dos objetos importantes: la cámara y la malla (o figura).
5. La malla se compone de geometría y material.
6. Para la geometría se usa un rectángulo de 1x1 creado mediante el objeto PlaneGeometry.
7. El material le indica a Three.js como rellenar al objeto, en este caso es de tipo MeshBasicMaterial (un color simple que por defecto es blanco).
8. El rectángulo tiene una posición por defecto en el origen, es decir, coordenada (0, 0, 0).
9. Finalmente se renderiza la escena invocando al método render(), alimentado con la escena y la cámara previamente definidas.
Una animación simple, I
FCC-BUAP 17
•Comencemos con la configuración de una escena que contiene un cubo giratorio. El código deeste ejemplo funcional se muestra a continuación, pero se hace énfasis en algunos puntos clave.Ejemplo 2-2. cubo rotatorio.
•Primero se crea la escena:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight,
0.1, 500 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x0000ff, 1 );
document.body.appendChild( renderer.domElement )
Una animación simple, II
FCC-BUAP 18
•Aunque en el ejemplo anterior ya se había utilizado un cámara, para este ejemplo cobra mayorrelevancia este objeto, y sus métodos, dado que ahora se trabaja en un entorno 3D.
•Por ahora se usará una “cámara en perspectiva”, donde los parámetros del métodoPerspectiveCamera son:
1. FOV: valor dado en grados que define el campo de visión.
2. Aspect Ratio: es la relación de aspecto, que usualmente se calcula dividiendo el ancho del puerto de visión entre el alto. Es útil
para conservar las proporciones en pantalla.
3. Near: determina la posición del plano de corte más cercano.
4. Far: determina la posición del plano de corte más lejano.
• Nótese que el objeto a renderizar debe quedar dentro del volumen definido por estos parámetros.
Una animación simple, III
FCC-BUAP 19
Una animación simple, IV
FCC-BUAP 20
•Se crea una luz direccional para que pueda existir color, brillo y sombra.
•Después se crea la geometría y el material para rellenar la malla creada a partir de la geometría.
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
light.position.set( 0, 0, 1 );
scene.add( light );
Una animación simple, V
FCC-BUAP 21
•Nótese que a diferencia del ejemplo anterior, ya no se utilizó el método MeshBasicMaterial,ahora con el método MeshPhongMaterial es posible definir un material para superficiesbrillantes con reflejos especulares.
•La función de animate crea un ciclo que provoca el renderizado de la escena cada vez que el la pantalla necesita ser actualizada (aprox. 60 veces por segundo), ya sea para renderizar o para animar.
function animate()
{
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
Una animación simple, VI
FCC-BUAP 22
•Una de las principales ventajas de usar la función requestAnimationFrame es que, adiferencia de una función “hecha a mano”, esta pone en pausa la aplicación cuando el usuariocambia de pestaña; por lo tanto no desperdicia tu precioso poder de procesamiento y duraciónde batería.
•Finalmente es necesario agregar un par de líneas más entre la funciónrequestAnimationFrame y la función render. Dado que necesitamos hacer rotar un cubo decolor verde, cuya geometría está definida en 3 dimensiones, es posible asignar un pequeñoincremento continuo para que rote en el eje X y en el eje Y al mismo tiempo.
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
Una animación simple, VII
FCC-BUAP 23
•El código completo se presenta a continuación:
<!DOCTYPE html>
<html>
<head>
<title>Cubo rotatorio con Three.js</title>
<meta charset="UTF-8"/>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="../build/three.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight,
0.1, 500 );
Una animación simple, VIII
FCC-BUAP 24
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x0000ff, 1 );
document.body.appendChild( renderer.domElement );
var light = new THREE.DirectionalLight( 0xffffff, 1.5);
light.position.set(0, 0, 1);
scene.add( light );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 4;
animate();
Una animación simple, IX
FCC-BUAP 25
function animate ()
{
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
</script>
</body>
</html>
Una animación simple, X
FCC-BUAP 26
Una aplicación 3D, I
FCC-BUAP 27
•Ignorando los detalles de configuración que se discutirán más adelante, y la agregación de hojasde estilo (CSS) para color de fondo y fuentes, el siguiente programa comienza de forma muysimilar a los ejemplos anteriores.
•Esta vez se utiliza el parámetro de antialias puesto en true en el constructor del objeto derenderizado, para decirle a Three.js que use un renderizado con antiliasing. Esto evita efectosdesagradables que provocan que los bordes de una figura aparezcan “dentados”.
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild( renderer.domElement );
Una aplicación 3D, II
FCC-BUAP 28
•Esta vez se utiliza el método set de la propiedad position del objeto de la cámara para establecersu poción.
Sombreado de la escena
•Es necesario incluir un poco de sombreado para distinguir los bordes de cada cara del cubo; paraobtener sombras en la escena se requieren dos cosas: una fuente de luz y un tipo de materialdistinto al básico.
camera.position.set( 0, 0, 3 );
Una aplicación 3D, III
FCC-BUAP 29
•La luz direccional con Three.js se ubica en una posición definida por el programador y apuntapor defecto al origen del sistema de coordenadas de la escena.
•El tipo de material BasicMaterial define atributos simples tales como color sólido otransparencia, pero no muestra sombras basadas en fuentes de luz.
var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
light.position.set( 0, 0, 1 );
scene.add( light );
Una aplicación 3D, IV
FCC-BUAP 30
•Por lo tanto, para poder ver los bordes del cubo es necesario utilizar el materialMeshPhongMaterial. Este tipo de material implementa un modelo eficiente de sombreadosimple y de aspecto bastante realista, llamado “Phong shading”.
•Hasta ahora no se han mencionado los shaders, esto es debido que Three.js los implementa pornosotros. Así que simplemente configuramos luces y materiales y Three.js utiliza sus shaderspre-programados haciendo el “trabajo sucio” por nosotros (¡gracias Mr.doob!).
var mapUrl = "images/molumen_small_funny_angry_monster.jpg";
var texture = new THREE.TextureLoader().load( mapUrl );
var material = new THREE.MeshPhongMaterial({map: texture});
Una aplicación 3D, V
FCC-BUAP 31
Asignación de un mapa de textura
•Las texturas son mapas de bits utilizados para representar atributos de superficies de mallas 3D.Pueden ser utilizados para definir desde simples colores hasta complejos efectos cuando secombinan.
•A diferencia de WebGL, Three.js proporciona un API simple y fácil de usar para cargar texturas yasociarlas con materiales.
Una aplicación 3D, VI
FCC-BUAP 32
•Se carga la textura desde un archivo de imagen, para lo cual se hace uso del constructorTHREE.TextureLoader y de su método load(), al cual se le pasa como argumento lavariable mapUrl que contiene la ruta de la imagen.
•Three.js está mapeando los bits de la imagen JPEG sobre las partes correspondientes de cadacara del cubo; nótese que la imagen no se está estirando para envolver el cubo, la imagentampoco aparece volteada hacia abajo ni tampoco invertida.
•Lo anterior podría parecer que no es gran cosa, pero si se tuviera que codificar algo como estoen WebGL nativo, habría muchos detalles que considerar.
Una aplicación 3D, VII
FCC-BUAP 33
Rotación del objeto
•Antes de poder ver el cubo en acción, vamos a girarlo en su eje X (horizontal) y en su eje Y(vertical); esto se hace configurando la propiedad rotation de la malla.
•En Three.js cada objeto puede tener una posición, una orientación y una escala. Por lo tanto, elprimer paso para configurar la orientación del cubo y que puedan verse más de una cara a la vezes como sigue:
cube.rotation.x = Math.PI / 5;
cube.rotation.y = Math.PI / 5;
Una aplicación 3D, VIII
FCC-BUAP 34
•NOTA:◦ En la mayoría de sistemas gráficos 3D los grados se representan en radianes, i.e. 2π = 360 grados.
◦ Por lo tanto, en Three.js la constante Math.PI es equivalente a 180 grados y Math.PI/5 es igual a 36grados.
El ciclo de renderizado con requestAnimationFrame()
•Hasta ahora se han realizado pocos cambios estructurales en el código, en comparación con elejemplo anterior. Así que primero se agregarán algunas funciones de apoyo y después sedefinirá un conjunto de variables globales para manejar información.
Una aplicación 3D, IX
FCC-BUAP 35
•Un ciclo de ejecución o ciclo de renderizado es importante para escenas que contienen uno ovarios objetos animados, o bien que deben estar “pendientes” de cambios basados en entradasde usuario.
•Existen un par de caminos para implementar un ciclo de renderizado.
◦ El primero es usar la función setTimeout() con un callback que renderice la escena y reinicie eltimeout. Este es el enfoque clásico de animación en la Web.
◦ Sin embargo, los navegadores más recientes soportan algo mejor: la invocación de la funciónrequestAnimationFrame(). Esta función ha sido diseñada específicamente para animación depáginas, incluyendo animación con WebGL.
Una aplicación 3D, X
FCC-BUAP 36
•Con requestAnimationFrame(), el navegador puede optimizar el rendimiento debido aque combinará cada solicitud en un sólo paso de redibujado.
•Finalmente, a continuación implementamos una función run() que implemente el ciclo derenderizado.
function run()
{
renderer.render( scene, camera );
if (animating)
{
cube.rotation.y -= 0.01;
}
requestAnimationFrame(run);
}
Una aplicación 3D, XI
FCC-BUAP 37
Dando vida a la página
•Hasta ahora hemos obtenido un bonito objeto en una página Web y verdaderamente 3D, pero alfinal del día los gráficos 3D no se tratan sólo del renderizado; se tratan también de la animacióne interactividad con el usuario.
•Por lo tanto, seria bueno “controlar” cuándo el cubo puede girar. Es por esto que se ha agregadoa la página un manejador de clics, a través del simple uso de DOM para el manejo de eventos.
•El truco es averiguar dónde agregar el manejador de eventos. En este caso, es el elemento DOMasociado con el objeto de renderizado de Three.js
Una aplicación 3D, XII
FCC-BUAP 38
El código completo se presenta a continuación…
function addMouseHandler()
{
var dom = renderer.domElement;
dom.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseUp(event)
{
event.preventDefault();
animating = !animating;
}
Una aplicación 3D, XIII
FCC-BUAP 39
<!DOCTYPE html>
<html>
<head>
<title>Bienvenido a WebGL</title>
<meta charset="UTF-8"/>
<link rel="stylesheet" href="../css/webglbook.css" />
<script src="../build/three.js"></script>
<script>
var renderer = null,
scene = null,
camera = null,
cube = null,
animating = false;
function onLoad()
{
var container = document.getElementById("container");
scene = new THREE.Scene();
Una aplicación 3D, XIV
FCC-BUAP 40
camera = new THREE.PerspectiveCamera( 45, container.offsetWidth /
container.offsetHeight, 1, 4000 );
camera.position.set( 0, 0, 3 );
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild( renderer.domElement );
var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
light.position.set( 0, 0, 1 );
scene.add( light );
var mapUrl = "images/molumen_small_funny_angry_monster.jpg";
var texture = new THREE.TextureLoader().load( mapUrl );
var material = new THREE.MeshPhongMaterial({map: texture});
var geometry = new THREE.CubeGeometry(1, 1, 1);
cube = new THREE.Mesh(geometry, material);
Una aplicación 3D, XV
FCC-BUAP 41
cube.rotation.x = Math.PI / 5;
cube.rotation.y = Math.PI / 5;
scene.add( cube );
addMouseHandler();
run();
}
function run()
{
renderer.render( scene, camera );
if (animating)
{
cube.rotation.y -= 0.01;
}
requestAnimationFrame(run);
}
Una aplicación 3D, XVI
FCC-BUAP 42
function addMouseHandler()
{
var dom = renderer.domElement;
dom.addEventListener( 'mouseup', onMouseUp, false);
}
function onMouseUp(event)
{
event.preventDefault();
animating = !animating;
}
</script>
</head>
<body onLoad="onLoad();" style="">
<center><h1>¡Bienvenido a WebGL!</h1></center>
<div id="container" style="width:95%; height:80%; position:absolute;"></div>
<div id="prompt" style="width:95%; height:6%; bottom:0; text-align:center;
position:absolute;">Clic para animar el cubo</div>
</body>
</html>
Una aplicación 3D, XVII
FCC-BUAP 43