inmensia |
Blog
Juan Mellado, 3 Septiembre, 2011 - 11:41
Vaya, ni un sólo post en julio y agosto. Resulta curioso, ya que es la época en que más tiempo libre tengo. En fin..., al grano. He estado trasteando un poco más con WebGL, haciendo cosas básicas, como un cargador de ficheros OBJ. Es un formato de texto plano del que siempre había leído que era fácil de implementar. Lo que no había leído es que en la práctica resulta difícil encontrar buenos modelos para probar. Y lo que es peor, ficheros OBJ bien construidos. Creo que hay por ahí un montón de plugins que no hacen muy bien su trabajo. Y un montón de gente que le da al botón de exportar y luego no se preocupa de lo que ha generado es un fichero que pueda ser de utilidad para alguien. Pero en fin, al mal tiempo buena cara, y a pensar que gracias a esto he podido aprender algunas cosas. Para la imagen de este post he utilizado este modelo low-poly de 6.227 triángulos. Después de trastear con él un rato me ha acabado extrañando un poco que tuviera tanta calidad para ser gratuito, así que me he puesto a buscar y resulta que pertenece a un juego llamado "Soul of the Ultimate Nation" (más conocido como S.U.N.), un MMORPG coreano. ![]() La imagen está generada directamente en Chrome con WebGL. El modelo de la izquierda sólo tiene aplicado las texturas de difusa. E incluso así luce bastante bien, porque las texturas son muy detalladas y, a mi juicio, están muy bien elaboradas. Tiene una textura para el pelo de 512x512, otra para la cara de 512x512 también, y una última para el cuerpo de 1024x1024. Al segundo modelo le he aplicado una iluminación muy sencilla, con una componente ambiental y una luz situada arriba a su izquierda. Mejora mucho con respecto al primero al insinuarse los volúmenes, en vez de verse tan plano, y el ojo/cerebro lo agradece. El tercer modelo tiene especular, calculado, sin texturas. Bastante exagerado quizás, pero es un efecto que siempre me ha gustado bastante. Queda muy bien sobre todo por los brillos que aparecen en el vientre y muslo derecho. Aunque otros brillos un tanto forzados, como el de brazo y pecho, quizás no deberían estar ahí. El OBJ no tiene referencias a ningún material, por lo que he tenido que inventarme los coeficientes de iluminación. Lo que si tiene son tres texturas con los mapas de normales, que a priori debería ser lo siguiente que implemente. Una pena que no tenga animaciones.
Juan Mellado, 17 Junio, 2011 - 18:58
Sigo cerrando temas. Esta vez le ha tocado el turno a la demo que tenía pendiente de hacer para demostrar el uso de js-openctm. Al final he tirado por lo más básico y he hecho un pequeño visor en WebGL para un modelo 3D con más de un millón y medio de triángulos. Demo online: La demo carga un modelo 3D en formato .ctm con 842.718 vértices y 1.685.024 triángulos, para ser exactos. El fichero pesa 6 megas, que aunque es muy poco para los estándares actuales, hace que mi hosting se resienta bastante. Al final tarda más en descargar desde la web que en descomprimir. Podría haber puesto un modelo más pequeño, pero es con un fichero de ese tamaño cuando se aprecia realmente el trabajo que realiza la librería. El modelo en su formato original ocupaba seis veces más. La malla del modelo es realmente densa. Aunque en el vídeo parezca lo contrario, el modelo no tiene ninguna textura aplicada, sólo color en los vértices. La gracia está en que es un modelo obtenido directamente de una persona de verdad mediante un escanner 3D de la empresa Cyberware. Supongo que de algún empleado "voluntario". Mi código de pruebas original que use cuando estaba desarrollando la librería creo que resultaría de poca utilidad a alguien ajeno a mí, así que he optado por adaptar uno de los tutoriales de learningwebgl.com, que es para WebGL lo que NeHe para OpenGL. Aunque me ha obligado a meter una dependencia en la demo con glmatrix, una librería JavaScript para operar con vectores y matrices. Para terminar, una cuestión técnica acerca de la implementación. En Firefox he podido (debido) utilizar un web worker para procesar el fichero en segundo plano, mientras que en Chrome no he podido porque actualmente no es capaz de traspasar Typed Arrays desde y hacía un web worker. No tiene implementada todavía la serialización de este tipo de objetos en JavaScript. He encontrado un issue al respecto, donde dice que están esperando que se defina mejor como tiene que ser el paso de este tipo de parámetros en la especificación. El inconveniente es que obliga a realizar el proceso en el hilo principal de la página, y en la mayoría de ordenadores es bastante probable que aparezca el típico mensaje avisando de que un proceso JavaScript está tardando más de la cuenta.
Juan Mellado, 13 Junio, 2011 - 11:03
Tenía pendiente hacer una prueba en la nueva versión de Firefox para comparar el rendimiento de js-openctm con respecto a Chrome, y la verdad es que el resultado ha sido bastante decepcionante. Firefox tarda del orden de cuatro veces más con los modelos grandes (1 millón y medio de polígonos). Y lo que es peor, aún no tiene implementado antialias. En la imagen puede verse un modelo renderizado con WebGL en Chrome (izquierda) y Firefox (derecha). ![]() Los característicos "dientes de sierra" que aparecen debido a la falta de antialias son bastante evidentes en el render de Firefox, aun cuando se supone que debería estar activado por defecto según se indica en la especificaciones de WebGL. He tratado de activarlo por software, a través de los "hints" que se le pueden pasar como parámetro a la hora de obtener el contexto, pero no ha servido para nada. canvas.getContext("webgl", {antialias:true});Buscando por Internet me he encontrado una respuesta del equipo de Firefox a este comportamiento. Y viene a decir que realmente lo del antialias no es obligatorio, sólo eso, un "hint". Que por ahora no lo tienen implementado, y que no les parece prioritario. Lo que si funciona es decirle a Chrome que desactive el antialias para que se vea igual de mal que en Firefox. Aunque no resulta de mucha utilidad, sólo para constatar que tiene implementada la gestión del antialias.
Juan Mellado, 12 Junio, 2011 - 10:32
Demo online:
Juan Mellado, 11 Junio, 2011 - 14:30
Después de unas cortas, pero merecidas vacaciones, he retomado el proyecto de realidad aumentada para darle los últimos toques finales. En la lista del TODO me quedaron unas cuantas cosas por hacer que creía importante revisar. Al final he conseguido quitarme las más grandes de en medio, aunque alguna de ellas me han dado algún que otro dolor de cabeza. Memoria ![]() Como se observa en la primera imagen, la memoria empieza con unos valores iniciales aceptables. No obstante, el problema viene cuando a partir de ahí la memoria reservada no hace más que crecer y crecer, hasta que al alcanzar el límite de 2GB se cuelga la pestaña del navegador. Un error que ya había detectado antes, y que me tenía algo mosqueado. ![]() En la segunda imagen se ve la memoria a punto de alcanzar el máximo y provocar que el proceso aborte. No obstante, en esa imagen hay algo aún más importante en lo que fijarse. Y es en el hecho de que la memoria reservada por JavaScript y Flash sigue siendo del mismo orden de magnitud que al principio. La buena noticia es que todo apunta a que el problema no lo está generando el programa, ya que ni JavaScript ni Flash aparentan ser los culpables. Y la mala es que entonces surge la duda: ¿quién diablos está reservando toda esa memoria? Después de revisar todo una y otra vez, al final me he dado cuenta de que la memoria sólo crece de esa forma cuando tengo abierta la ventana de depuración de Chrome. O sea, que el problema es del navegador, no mía (\o/). Cuando tengo abierta la ventana de depuración (lo que en mi caso es prácticamente el 100% de las veces) la memoria no hace más que crecer y crecer. Pero cuando la tengo cerrada, haciendo una navegación normal, la memoria mantiene sus valores iniciales. ¡Aclarado el misterio! Rendimiento
Mi webcam, la más barata que encontré, es capaz de tomar imágenes a un ritmo de 15 fotogramas por segundo. Lo que da un tiempo de proceso de unos 67 (= 1000 / 15) milisegundos entre un fotograma y otro, mientras que librería estaba tardando 300 milisegundos por fotograma. La buena noticia es que al desplegar el detalle de los tiempos se observaba que en realidad el proceso de detección de marcadores apenas demoraba 40 milisegundos. Así que volvía a surgir una nueva pregunta: ¿en qué se estaba yendo el resto del tiempo? Pues resulta que todo el tiempo se consume en enviar los fotogramas desde Flash a JavaScript. Y por desgracia estoy obligado a utilizar Flash para capturar el vídeo, ya que todavía no puede hacer directamente desde JavaScript. Resulta que el paso de información entre Flash y JavaScript es bastante lento en general por culpa del proceso de serialización. Cuando implementé la librería de captura de vídeo en Flash me limité a guardar la imagen capturada en un Array y pasárselo a JavaScript, pero eso a la larga ha resultado ser muy sencillo de implementar pero muy poco eficiente.
Para acelerar el proceso de transferencia de datos entre Flash y JavaScript he realizado un montón de pruebas. Al final, la mejor implementación que he conseguido realizar, en cuanto a rendimiento, ha consistido en convertir todos los pixels de la imagen a cadena de caracteres y concatenarlos en un String separados por comas. Muy tosco, pero efectivo. Para disminuir el tamaño de la cadena de caracteres lo que he hecho es enviar la diferencia de un pixel con respecto al anterior, ya que normalmente tienden a parecerse. Y para reducir un poco más el número de caracteres los he codificado en base 36. Esto último puede sonar raro, pero es que esa es la mayor conversión de base que realizan de forma nativa tanto como Flash como JavaScript. De esta forma he encontrado un equilibrio entre la cantidad de información a intercambiar y el tiempo necesario para procesarla. He subido la clase ActionScript en un nuevo proyecto que he creado llamado flashcam. Por último, y entrando un poco más en detalle acerca de los tiempos de proceso de la librería, la función que más tarda con diferencia es la que realiza el filtro gaussiano, ya que ella sola se lleva el 75% del tiempo de ejecución. Curioso que al final una función "auxiliar" sea la que más tiempo tarde. Habrá que buscarle una alternativa. |