{"id":2209,"date":"2019-06-16T23:41:01","date_gmt":"2019-06-16T23:41:01","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2209"},"modified":"2020-02-15T19:06:18","modified_gmt":"2020-02-15T19:06:18","slug":"1984-mi-pequeno-homenaje-a-un-paper-iconico-del-cgi","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2209","title":{"rendered":"1984: mi peque\u00f1o homenaje a un paper ic\u00f3nico del CGI"},"content":{"rendered":"\n<p>En 1984, hace ya la friolera de 35 a\u00f1os, un grupo de investigadores de la divisi\u00f3n de gr\u00e1ficos por ordenador de <a href=\"https:\/\/www.lucasfilm.com\/\">Lucasfilm<\/a> (lo que un par de a\u00f1os despu\u00e9s pasar\u00eda a llamarse <a href=\"https:\/\/www.pixar.com\/\">P\u00edxar<\/a>) public\u00f3 un <em>paper<\/em> donde present\u00f3 la soluci\u00f3n definitiva a muchos de los problemas del momento de los gr\u00e1ficos por ordenador orientados al cine y al entretenimiento. Me refiero al <a href=\"http:\/\/graphics.pixar.com\/library\/DistributedRayTracing\/paper.pdf\">Distributed Ray Tracing<\/a>. Esta t\u00e9cnica permit\u00eda a\u00f1adir a los gr\u00e1ficos por ordenador hechos mediante <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ray_tracing_(graphics)\">Ray Tracing<\/a> elementos fotorrealistas tales como <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/Motion_blur\">motion blur<\/a><\/em>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Depth_of_field\">profundidad de campo<\/a>, sombras con penumbras, antialiasing, y m\u00e1s, y todo ello sin a\u00f1adir pr\u00e1cticamente carga computacional extra. Esa era su gran novedad.<\/p>\n\n\n\n<p>Es cierto que el Ray Tracing es una t\u00e9cnica tan costosa a nivel de procesador y, sobre todo, de memoria, que, durante d\u00e9cadas, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pixar_RenderMan\">Renderman<\/a>, el render desarrollado por P\u00edxar y utilizado en la inmensa mayor\u00eda de escenas de pel\u00edculas hechas por ordenador, no la utilizaba, sino que se basaba en la t\u00e9cnica <a href=\"https:\/\/en.wikipedia.org\/wiki\/Reyes_rendering\">REYES (Renders Everything You Ever Saw)<\/a>. Era la \u00fanica manera de poder hacer gr\u00e1ficos fotorrealistas en los equipos de los 80, los 90 y buena parte de los 2000. Sin embargo, algunas de las t\u00e9cnicas desarrolladas en el <em>paper<\/em> se pudieron adaptar a dicho algoritmo, espec\u00edficamente el <em>motion blur<\/em> y la profundidad de campo. Hoy en d\u00eda, en cambio, s\u00ed que se utiliza Ray Tracing de manera rutinaria, basado en mejoras de las t\u00e9cnicas presentadas en este <em>paper<\/em>. As\u00ed trabajan hoy en d\u00eda Ray Tracers comerciales como <a href=\"https:\/\/en.wikipedia.org\/wiki\/Arnold_(software)\">Arnold<\/a>, o el propio Renderman a partir de su <a href=\"http:\/\/www.cgchannel.com\/2014\/11\/pixar-ships-renderman-19\/\">versi\u00f3n 19<\/a>.<\/p>\n\n\n\n<p>Sin embargo, adem\u00e1s de todo esto, al final de dicho <em>paper<\/em> aparece una imagen que muestra las capacidades de los algoritmos descritos en \u00e9l, y que ya se puede considerar ic\u00f3nica. Dicha imagen se titula <em>1984<\/em> y muestra una mesa de billar con cinco bolas, las cuales exhiben los efectos de <em>motion blur<\/em>, sombras suaves, reflectividad (lo que permite ver la ventana de la sala y a una persona sosteniendo un taco)&#8230;<\/p>\n\n\n\n<p>\u00bfY a qu\u00e9 viene toda esta chapa? Pues a que he querido hacer un peque\u00f1o homenaje a dicha publicaci\u00f3n y a los genios detr\u00e1s de ella programando un peque\u00f1o Ray Tracer propio con el que reproducir dicha imagen. Y \u00e9ste es el resultado:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1.png\" rel=\"lightbox-0\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1-1024x576.png\" alt=\"\" class=\"wp-image-2211\" width=\"582\" height=\"327\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1-1024x576.png 1024w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1-300x169.png 300w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1-768x432.png 768w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2019\/06\/1984-1.png 1920w\" sizes=\"auto, (max-width: 582px) 100vw, 582px\" \/><\/a><figcaption>Mi versi\u00f3n\/homenaje. Pulsa sobre ella para ampliarla.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Al igual que en la imagen original, todas las bolas son reflectantes (lo que permite apreciar el entorno de la sala), y cuatro de ellas tienen <em>motion blur<\/em>: algunas (como la blanca y la bola 9) tienen un motion blur sencillo, y las otras dos, la 8 y la 4, uno m\u00e1s complejo, al estar detenidas durante parte del tiempo de apertura del <em>obturador<\/em> y moverse s\u00f3lo durante parte del <em>tiempo de exposici\u00f3n<\/em>. Por supuesto, no cabe esperar una reproducci\u00f3n fidedigna (<em>\u00a1Soy programador, Jim, no artista!<\/em>), pero lo importante para m\u00ed no es tanto la imagen en s\u00ed, sino el hecho de que he sido capaz de programar un Ray Tracer desde cero. Desde que le\u00ed el <em>paper<\/em> original qued\u00e9 prendado de la elegancia del algoritmo, y ten\u00eda ganas de intentar implementarlo.<\/p>\n\n\n\n<p>Sin embargo, lo <em>divertido<\/em> (relativamente) es que acab\u00e9 escribiendo no uno, sino dos Ray Tracers: cuando empec\u00e9 con este proyecto, hace un par de semanas, a\u00fan no ten\u00eda muy claro el nivel de complejidad que me iba a encontrar, as\u00ed que decid\u00ed no complicarme la vida m\u00e1s de lo estrictamente necesario y opt\u00e9 por utilizar <em><a href=\"https:\/\/www.python.org\/\">Python<\/a><\/em> para escribirlo, junto con <em><a href=\"https:\/\/www.numpy.org\/\">numpy<\/a><\/em> para toda la parte de vectores y matrices que necesitaba. Primero constru\u00ed un <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/Ray_casting\">Ray Caster<\/a><\/em> capaz de mostrar esferas y planos. Luego le a\u00f1ad\u00ed soporte de mapeado de texturas. A continuaci\u00f3n antialiasing y soporte de luces. Llegado a este punto a\u00f1ad\u00ed soporte de <em>motion blur<\/em> y empec\u00e9 a preparar ya la escena de la sala de billar en lugar de las im\u00e1genes de demostraci\u00f3n con una <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/Cornell_box\">Cornell Box<\/a><\/em>.<\/p>\n\n\n\n<p>El resultado es el de ah\u00ed arriba. Sin embargo, ten\u00eda un problema serio: es <em>desesperadamente lento<\/em>. Es cierto que, como dije antes, la t\u00e9cnica de Ray Tracing es muy costosa computacionalmente, pero es que hablamos de \u00a118 horas para esa imagen a 1920&#215;1080! \u00a1Y eso que ya hab\u00eda hecho unas cuantas optimizaciones que me permitieron reducir los tiempos a, aproximadamente, dos tercios de los originales, adem\u00e1s de utilizar todos los n\u00facleos de mi ordenador! Es m\u00e1s o menos lo que habr\u00eda tardado a principios de los 90 con <a href=\"http:\/\/povray.org\/\">PovRay<\/a> y un 8086.<\/p>\n\n\n\n<p>Fue por esto que, aunque el objetivo original ya estaba cumplido, tom\u00e9 la decisi\u00f3n de portar el c\u00f3digo a C++ para ver cuanto pod\u00eda ara\u00f1ar. Quer\u00eda saber si el problema estaba en Python o en mi c\u00f3digo. Aunque dicho as\u00ed puede sonar a locura, lo cierto es que el c\u00f3digo de Python era bastante sencillo (actualmente son un total de 1415 l\u00edneas de c\u00f3digo, que, tras quitar l\u00edneas en blanco y comentarios, se quedan en 899), con lo que era factible coger cada uno de los ficheros y <em>traducirlo<\/em> de un lenguaje a otro. S\u00f3lo tuve que escribir un par de clases para trabajar con vectores y matrices y alguna cosa m\u00e1s, lo que no fue especialmente dif\u00edcil. Es cierto que probablemente podr\u00eda haber usado cualquiera de las alternativas que seguro que hay por ah\u00ed ya escritas, pero dado que s\u00f3lo tengo que trabajar con matrices y vectores de tama\u00f1o fijo, y s\u00f3lo necesito un pu\u00f1ado de operaciones muy concretas, era m\u00e1s trabajo aprender a usarlas que escribirlo yo. Y encima es probable que me diese m\u00e1s rendimiento (repito: <em>tama\u00f1o fijo<\/em>).<\/p>\n\n\n\n<p>El resultado fue, en una sola palabra: <strong>pasmoso<\/strong>. De las m\u00e1s de 18 horas pas\u00e9 a 398 <em>segundos<\/em>. Repito: <em>menos de siete minutos para la misma imagen<\/em>: 1920&#215;1080 y <em>supersampling<\/em> de 8&#215;8.<\/p>\n\n\n\n<p>S\u00ed es cierto que me encontr\u00e9 con un problema algo raro: en Python utilic\u00e9 <em>fork()<\/em> para poder aprovechar todos mis n\u00facleos porque, como es de sobra conocido, el <a href=\"https:\/\/en.wikipedia.org\/wiki\/Global_interpreter_lock\">GIL<\/a> no permite que varios threads se ejecuten de manera concurrente. Esto complic\u00f3 bastante el c\u00f3digo, pues tuve que usar <em>pipes<\/em> para que el programa principal fuese repartiendo nuevas l\u00edneas a cada proceso hijo a medida que iban terminando y quedando libres, y tambi\u00e9n para que \u00e9stos enviasen las l\u00edneas ya renderizadas al padre para que las uniese en una sola imagen. Para la versi\u00f3n en C++ no quer\u00eda complicarme tanto, as\u00ed que decid\u00ed utilizar <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX_Threads\">pthreads<\/a> desde el principio, de manera que todos los hijos accediesen a la misma memoria para ir depositando sus l\u00edneas en el punto correcto sin tener que enviarlas mediante <a href=\"https:\/\/en.wikipedia.org\/wiki\/Inter-process_communication\">IPC<\/a>, y autoasign\u00e1ndose la siguiente l\u00ednea mediante un <a href=\"https:\/\/en.wikipedia.org\/wiki\/Semaphore_(programming)\">sem\u00e1foro<\/a>. Sin embargo, algo raro ocurr\u00eda: tardaba m\u00e1s que cuando utilizaba un \u00fanico proceso. Si no fuese porque <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/Top_(software)\">top<\/a><\/em> indicaba una carga de procesador del 400%, y que el procesador indicaba que hab\u00eda seis threads de mi programa, habr\u00eda asegurado que estaba utilizando una biblioteca de threads en espacio de usuario, sin threads reales.<\/p>\n\n\n\n<p>Despu\u00e9s de muchas pruebas, de quemarme las pesta\u00f1as viendo el c\u00f3digo, y buceando en <a href=\"https:\/\/stackoverflow.com\/\">Stack Overflow<\/a> y otros sitios, no consegu\u00ed descubrir qu\u00e9 ocurr\u00eda, as\u00ed que decid\u00ed utilizar tambi\u00e9n <em>fork()<\/em> en C++, pero a\u00f1adiendo memoria compartida para simplificar la comunicaci\u00f3n y la complejidad al m\u00e1ximo, y ah\u00ed s\u00ed que consegu\u00ed la ganancia de rendimiento esperada; esto es, multiplicar la velocidad por (pr\u00e1cticamente) el n\u00famero de n\u00facleos del ordenador.<\/p>\n\n\n\n<p>Y esto es m\u00e1s o menos todo. Aquellos que quieran curiosear un poco en mi c\u00f3digo, lo tienen disponible en mi repositorio git:<\/p>\n\n\n\n<p class=\"has-text-align-center\"><a href=\"https:\/\/gitlab.com\/rastersoft\/1984\">https:\/\/gitlab.com\/rastersoft\/1984<\/a><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>En 1984, hace ya la friolera de 35 a\u00f1os, un grupo de investigadores de la divisi\u00f3n de gr\u00e1ficos por ordenador de Lucasfilm (lo que un par de a\u00f1os despu\u00e9s pasar\u00eda a llamarse P\u00edxar) public\u00f3 un paper donde present\u00f3 la soluci\u00f3n definitiva a muchos de los problemas del momento de los gr\u00e1ficos por ordenador orientados al &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2209\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">1984: mi peque\u00f1o homenaje a un paper ic\u00f3nico del CGI<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,5],"tags":[],"class_list":["post-2209","post","type-post","status-publish","format-standard","hentry","category-cacharreo","category-programacion"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2209","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2209"}],"version-history":[{"count":16,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2209\/revisions"}],"predecessor-version":[{"id":2291,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2209\/revisions\/2291"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}