diff --git a/docs/index.html b/docs/index.html index edd2589..43d343a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3961,12 +3961,8 @@

Por fortuna, hay algoritmos que son capaces de proporcionar la constante \(s\) sin necesidad de calcular la integral. Uno de los más conocidos es -Metropolis-Hastings, el cual se basa en cadenas de -Markov de Monte Carlo. Sin embargo, su complejidad hace que se escape -del ámbito de este trabajo. Se puede encontrar más información en (Pharr, Jakob, and Humphreys 2016, Metropolis -Light Transport).

+Metropolis-Hastings, del cual hablaremos +posteriormente.

En este trabajo nos centraremos en buscar funciones de densidad \(p_X\) que se aproximen a \(f\) lo más fielmente posible, dentro del @@ -4673,6 +4669,18 @@

donde \(1 \le i \le N\), \(t, s \ge 0\).

+

Un algoritmo basado en cadenas de Markov muy famoso es el +muestreo de Metrópolis (Pharr, +Jakob, and Humphreys 2016, Metropolis Light Transport). Una +de sus grandes ventajas es que puede generar muestras que sigan la +distribución de valores de una función \(f\). Para hacerlo, no necesita nada más que +evaluar dicha función, sin necesidad de integrarla. Además, en cada +iteración del algoritmo se consiguen muestras usables, a diferencia de +otros método como aceptación-rechazo. Tal es su potencia que ha creado +un área específica en la informática gráfica, denominado Metropolis +light transport.

3.5.3 Resolviendo las ecuaciones de @@ -4926,9 +4934,10 @@

-

La implementación estará basada en Vulkan, junto al pequeño framework -de nvpro-samples, que puede encontrarse en el repositorio (La implementación estará basada en Vulkan, junto al pequeño +framework de nvpro-samples, que puede encontrarse en +el repositorio (NVIDIA 2022a). Nuestro trabajo recogerá varias de las características que se muestran en dicho repositorio. Además, @@ -5077,20 +5086,21 @@

Como es natural, el tiempo es una limitación enorme para cualquier programa en tiempo real. Mientras que en un offline renderer -disponemos de un tiempo muy considerable por frame (desde varios -segundos hasta horas), en un programa en tiempo real necesitamos que un -frame salga en 33.3 milisegundos o menos. Este concepto se suele -denominar frame budget: la cantidad de tiempo que disponemos -para un frame.

+disponemos de un tiempo muy considerable por frame (desde +varios segundos hasta horas), en un programa en tiempo real necesitamos +que un frame salga en 33.3 milisegundos o menos. Este concepto +se suele denominar frame budget: la cantidad de tiempo que +disponemos para un frame.

Nota: cuando hablamos del tiempo disponible para un -frame, solemos utilizar milisegundos (ms) o frames por segundo (FPS). -Para que un programa en tiempo real vaya suficientemente fluido, -necesitaremos que el motor corra a un mínimo de 30 FPS (que equivalen a -33.3 ms/frame). Hoy en día, debido al avance del área en campos como los -videosjuegos, el estándar se está convirtiendo en 60 FPS (16.6 -ms/frame). Aún más, en los videojuegos competitivos profesionales se han -asentado los 240 FPS (4.1 ms/frame).

+frame, solemos utilizar milisegundos (ms) o frames por +segundo (FPS). Para que un programa en tiempo real vaya suficientemente +fluido, necesitaremos que el motor corra a un mínimo de 30 FPS (que +equivalen a 33.3 ms/frame). Hoy en día, debido al avance del +área en campos como los videosjuegos, el estándar se está convirtiendo +en 60 FPS (16.6 ms/frame). Aún más, en los videojuegos +competitivos profesionales se han asentado los 240 FPS (4.1 +ms/frame).

Las nociones de los capítulos anteriores no distinguen entre un motor en tiempo real y offline. Como es natural, necesitaremos @@ -5168,7 +5178,7 @@

4.2.2 Frameworks y API de ray tracing en tiempo real

Una vez hemos cumplido los requisitos de hardware, es hora de escoger -los frameworks de trabajo.

+los frameworks de trabajo.

Las API de gráficos están empezando a adaptarse a los requisitos del tiempo real, por lo que cambian frecuentemente. La mayoría adquirieron las directivas necesarias muy recientemente. Aun así, son lo @@ -5223,8 +5233,8 @@

4.3 Setup del proyecto

Un proyecto de Vulkan necesita una cantidad de código inicial considerable. Para acelerar este trámite y partir de una base más -sólida, se ha decidido usar un pequeño framework de trabajo de Nvidia -llamado [nvpro-samples] framework de trabajo de +Nvidia llamado [nvpro-samples] (NVIDIA 2022b).

Esta serie de repositorios de Nvidia DesignWorks contienen proyectos @@ -5243,8 +5253,8 @@

glTF Scene altamente modificado. A su vez, es una simplificación del repositorio vk_raytrace. -

Estos frameworks contienen asimismo otras utilidades menores. -Destacan GLFW (gestión de ventanas en C++), +

Estos frameworks contienen asimismo otras utilidades +menores. Destacan GLFW (gestión de ventanas en C++), imgui (interfaz de usuario) y tinyobjloader (carga de .obj y .mtl).

@@ -5285,7 +5295,7 @@

  • Toda la implementación relacionada con el motor (y por tanto, Vulkan), se halla en engine.h/cpp. Una de las desventajas -de seguir un framework “de juguete” es que el acoplamiento es +de seguir un framework “de juguete” es que el acoplamiento es considerablemente alto. Más adelante comentaremos los motivos.
  • Los parámetros de la aplicación (como tamaño de pantalla y otras estructuras comunes) se encuetran en globals.hpp.
  • @@ -5325,9 +5335,9 @@

    4.3.2 Diagramas

    -

    Teniendo en cuenta que utilizamos un framework que no está pensado -para producción y la naturaleza de Vulkan, realizar un diagrama de clase -es muy complicado. Sin embargo, podemos ilustrar las clases más +

    Teniendo en cuenta que utilizamos un framework que no está +pensado para producción y la naturaleza de Vulkan, realizar un diagrama +de clase es muy complicado. Sin embargo, podemos ilustrar las clases más importantes de la aplicación: la el motor [28] y la de escenas [29] . En las secciones @@ -5362,18 +5372,18 @@

    Ejecuta los siguientes comandos desde la terminal para compilar el proyecto:

    -
    $ git clone --recursive --shallow-submodules https://github.com/Asmilex/Raytracing.git
    -$ cd .\Raytracing\application\vulkan_ray_tracing\
    -$ mkdir build
    -$ cd build
    -$ cmake ..
    -$ cmake --build .
    +
    git clone --recursive --shallow-submodules https://github.com/Asmilex/Raytracing.git
    +cd .\Raytracing\application\vulkan_ray_tracing\
    +mkdir build
    +cd build
    +cmake ..
    +cmake --build .

    Si todo funciona correctamente, debería generarse un binario en ./application/bin_x64/Debug llamado asmiray.exe. Desde la carpeta en la que deberías encontrarte tras seguir las instrucciones, puedes conseguir ejecutarlo con

    -
    $ ..\..\bin_x64\Debug\asmiray.exe
    +
    ..\..\bin_x64\Debug\asmiray.exe

    4.5 Estructuras de aceleración

    El principal coste de ray tracing es el cálculo de las intersecciones @@ -5393,12 +5403,13 @@

    (Shirley 2020b)) es la Bounding Volume Hierarchy -(BVH). Fue una técnica desarrollada por Kay y Kajilla en 1986. -Este método encierra un objeto en una caja (denomina una -bounding box), de forma que el test de intersección -principal se hace con la caja y no con la geometría. Si un rayo impacta -en la bounding box, entonces se pasa a testear la -geometría.

    +(BVH). Fue una técnica desarrollada por (Kay and Kajiya 1986). Este método +encierra un objeto en una caja (denomina una bounding +box), de forma que el test de intersección principal se hace +con la caja y no con la geometría. Si un rayo impacta en la bounding +box, entonces se pasa a testear la geometría.

    Se puede repetir esta idea repetidamente, de forma que agrupemos varias bounding boxes. Así, creamos una jerarquía de objetos –como si nodos de un árbol se trataran–. A esta jerarquía es a la que @@ -5546,10 +5557,10 @@

    application/vulkan_ray_tracing/src/shaders/raytrace.rgen. -
  • Closest hit shader: este shader se ejecuta en la -primera intersección con alguna geometría válida de la escena. Se pueden -trazar rayos recursivamente desde aquí (por ejemplo, para calcular -oclusión ambiental). El archivo correspondiente es +
  • Closest hit shader: se ejecuta en la primera +intersección con alguna geometría válida de la escena. Se pueden trazar +rayos recursivamente desde aquí (por ejemplo, para calcular oclusión +ambiental). El archivo correspondiente es application/vulkan_ray_tracing/src/shaders/raytrace.rchit.
  • Any-hit shader: similar al closest hit, pero invocado en cada intersección del camino del rayo que cumpla application/vulkan_ray_tracing/src/shaders/raytrace_rahit.glsl.
  • Miss shader: si el rayo no choca con ninguna geometría, se ejecuta este shader. Normalmente, añade una pequeña -contribución ambiental al rayo. Se halla +contribución ambiental al rayo. Se halla en application/vulkan_ray_tracing/src/shaders/raytrace.rmiss.
  • Intersection shader: este shader es algo diferente al resto. Su función es calcular el punto de impacto de un rayo con una -geometría. Por defecto se utiliza un test triángulo - rayo. En nuestro +geometría. Por defecto se utiliza un test triángulo-rayo. En nuestro path tracer lo dejaremos por defecto, pero podríamos definir algún método como los que vimos en la sección intersecciones rayo - -objeto.
  • +href="#intersecciones-rayo---objeto">intersecciones +rayo-objeto.

    Existe otro tipo de shader adicional denominado callable shader. Este es un shader que se invoca desde otro shader, a @@ -5639,7 +5650,8 @@

  • Compilar los dos arrays anteriores más un pipeline layout para -generar un vkCreateRayTracingPipelineKHR.
  • +generar pipeline de ray tracing mediante la función +vkCreateRayTracingPipelineKHR.
  • Conseguir los handlers de los shaders usando vkGetRayTracingShaderGroupHandlesKHR.
  • Alojar un buffer con el bit @@ -5661,23 +5673,24 @@

    ) -

    Cada entrada de la SBT contiene un handler y una serie de parámetros -embebidos. A esto se le conoce como Shader Record. -Estos records se clasifican en:

    +

    Cada entrada de la SBT contiene un handler y una serie de +parámetros embebidos. A esto se le conoce como Shader +Record. Estos records se clasifican en:

      -
    • Ray generation record: contiene el handler del ray -generation shader.
    • -
    • Hit group record: se encargan de los handlers del -closest hit, anyhit (opcional), e intersection (opcional).
    • +
    • Ray generation record: contiene el handler +del ray generation shader.
    • +
    • Hit group record: se encargan de los +handlers del closest hit, anyhit (opcional), e intersection +(opcional).
    • Miss group record: se encarga del miss shader.
    • Callable group record: para los shaders de tipo callable.

    Una de las partes más difíciles de la SBT es saber cómo se relacionan -record y geometría. Es decir, cuando un rayo impacta en una geometría, -¿a qué record de la SBT llamamos? Esto se determina mediante los -parámetros de la instancia, la llamada a trace rays, y el orden -de la geometría en la BLAS.

    +record y geometría. Es decir, cuando un rayo impacta en una +geometría, ¿a qué record de la SBT llamamos? Esto se determina mediante +los parámetros de la instancia, la llamada a trace rays, y el +orden de la geometría en la BLAS.

    Para conocer a fondo cómo funciona la Shader Binding Table, puedes visitar (Usher 2021, 193) @@ -5700,8 +5713,8 @@

  • Configura correctamente cada shader group.
  • Prepara las push constants.
  • -
  • Hace el setup del pipeline layout junto a sus descriptor -sets.
  • +
  • Hace el setup del pipeline layout junto a sus +descriptor sets.
  • Limpia la información innecesaria creada por la función.
  • El formato de materiales y objetos usados es el Wavefront (.obj). Aunque es un sistema relativamente antiguo y sencillo, se han usado definiciones específicas -en los materiales para adaptarlo a Physically Based Rendering. Entre los -parámetros del archivo de materiales .mtl, destacan:

    +en los materiales para adaptarlo a Physically Based Rendering. +Entre los parámetros del archivo de materiales .mtl, +destacan:

    • \(K_a \in [0, 1]^3\): representa el color ambiental. Dado que esto es un path tracer físicamente realista, @@ -6062,7 +6076,7 @@

      \[ \begin{aligned} - h & = 0 + e_1 w = e_1 \\ + h & = 0 + e_1 w = e_1, \\ w & = \frac{f_1 \cos\theta_1}{p_1} \end{aligned} \]

      @@ -6070,7 +6084,7 @@

      \[ \begin{aligned} h & = e_1 + e_2 w = \\ - & = e_1 + e_2 \frac{f_1 \cos\theta_1}{p_1} \\ + & = e_1 + e_2 \frac{f_1 \cos\theta_1}{p_1}, \\ w & = \frac{f_1 \cos\theta_1}{p_1} \frac{f_2 \cos\theta_2}{p_2} \end{aligned} \]

      @@ -6082,7 +6096,7 @@

      supersampling. Es decir, promediando el color de los últimos \(N\) frames para generar el frame actual. -Esta técnica se llama acumulación temporal.

      +class="math inline">\(N\)
      frames para generar el frame +actual. Esta técnica se llama acumulación temporal.

      Es importante destacar que la acumulación temporal solo es válido cuando la cámara se queda estática. Al cambiar de posición, la información del píxel se ve alterada significativamente, @@ -6130,25 +6144,27 @@

      (NVIDIA 2022a, jitter camera). Debemos modificar tanto el motor como -los shaders para llevar el recuento del número de frames en las push -constants.

      -

      Definimos el número máximo de frames que se pueden acumular:

      +los shaders para llevar el recuento del número de frames en las +push constants.

      +

      Definimos el número máximo de frames que se pueden +acumular:

      // engine.h
       class Engine {
           //...
           int m_maxAcumFrames {100};
       }
      -

      Las push constant deberán llevar un registro del frame en el que se -encuentran, así como un número máximo de muestras a acumular para un -pixel:

      +

      Las push constant deberán llevar un registro del frame en el +que se encuentran, así como un número máximo de muestras a acumular para +un pixel:

      // host_device.h
       struct PushConstantRay {
           //...
           int   frame;
           int   nb_samples
       }
      -

      El número de frame se reseteará cuando la cámara se mueva, la ventana -se reescale, o se produzca algún efecto similar en la aplicación.

      +

      El número de frame se reseteará cuando la cámara se mueva, +la ventana se reescale, o se produzca algún efecto similar en la +aplicación.

      Finalmente, en los shaders podemos implementar lo siguiente:

      // raytrace.rgen
       vec3 pixel_color = vec3(0);
      @@ -6263,7 +6279,7 @@ 

      .

      Nosotros no nos preocuparemos especialmente por esto. Este no es un trabajo sobre teoría del color, aunque nos metamos en varias partes en -ella. El área de tone mapping es extensa y merecería su propio +ella. El área de tone mapping es extensa y merecería su propio estudio.

      Es importante mencionar que sin acumulación temporal, el código anterior produciría variaciones significativas para pequeños @@ -6273,9 +6289,10 @@

      5 Análisis de rendimiento

      En este capítulo vamos a analizar el resultado final del proyecto. -Estudiaremos cómo se ve el motor, cómo rinde en términos de **frame* -time*, y compararemos las imágenes producidas con otras similares; tanto -producidas por otros motores, como situaciones en la vida real.

      +Estudiaremos cómo se ve el motor, cómo rinde en términos de tiempo de +procesado de un frame, y compararemos las imágenes producidas +con otras similares; tanto producidas por otros motores, como +situaciones en la vida real.

      5.1 Usando el motor

      Una vez se ha compilado el @@ -6353,7 +6370,7 @@

      -cube_reflective +cube_ reflective Ejemplifica ray traced reflections. @@ -6431,8 +6448,8 @@

      Ten en cuenta que las imágenes de las escenas no son definitivas. -Están sujetas a cambios, pues se podría cambiar el comportamiento de los -shaders.

      +Están sujetas a cambios, pues los shaders todavía se encuentran en +desarrollo.

      5.2 Exhibición de path tracing

      A lo largo de este trabajo hemos visto una gran variedad de conceptos @@ -6443,7 +6460,7 @@

      Empecemos por materiales. Se han implementado unos cuantos tipos diferentes, los cuales veremos ilustrados a continuación.

      Los más simples son los difusos. La caja de Cornell +href="#reflexión-difusa-o-lambertiana">difusos. La caja de Cornell original contiene dos objetos de este tipo:

      @@ -6560,8 +6577,8 @@

      5.2.3 Iluminación global

      -

      La iluminación global es un fenómeno físico que se -refiere a luz que proviene de todas direcciones. Es el efecto +

      La iluminación global es un fenómeno físico +referente a luz que proviene de todas direcciones. Es el efecto que propicia el rebote constante de los fotones emitidos por fuentes de luz hacia una escena, adquiriendo las propiedades de los materiales en los que rebotan.

      @@ -6646,12 +6663,12 @@

      +alt="Figura 48: Los lightmaps requieren que la geometría no pueda ser movida. Esto se debe a que la sombra no es calculada en tiempo real. Fuente: (Epic Games 2022b)" /> +lightmaps requieren que la geometría no pueda ser movida. Esto +se debe a que la sombra no es calculada en tiempo real. Fuente: (Epic Games 2022b)

      Para un vistazo más a fondo de la iluminación global, puedes @@ -6707,7 +6724,7 @@

      \(N\). Vemos cómo un número bajo de muestras (alrededor de 5) produce un frametime de aproximadamente 12 milisegundos, lo cual -corresponde a 83 frames por segundo. Duplicando frames por segundo. Duplicando \(N\) hasta las 10 muestras, produce un aumento del frametime hasta los 20 ms de media (50 FPS). Algo similar pasa con el resto de valores: 15 muestras suponen una media de @@ -6728,7 +6745,7 @@

      \[ \text{tiempo de renderizado} \approx a + N t \text{ -(milisegundos/*frame*)} +(milisegundos/frame)} \]

      Donde \(N \in \mathbb{N}\) es el número de muestras y \(t\) es el tiempo @@ -6766,9 +6783,9 @@

      -

      No obstante, el cambio de 10 a 20 no es tan significativo como de 1 a -5. Esto sugiere que debemos usar otras técnicas para reducir la varianza -del estimador. ¡La fuerza bruta no suele ser la solución!

      +

      No obstante, el cambio de 10 a 20 no supone un salto tan grande como +el de 1 a 5. Esto sugiere que debemos usar otras técnicas para reducir +la varianza del estimador. ¡La fuerza bruta no es la solución!

      5.3.2 Profundidad de un rayo

      Una de las decisiones que tenemos que tomar en el diseño del @@ -6783,9 +6800,9 @@

      +alt="Figura 52: Coste de un frame en función de la profundidad del camino" /> +frame en función de la profundidad del camino

      En esta figura [52] ocurre algo @@ -6804,10 +6821,11 @@

      depth = 10, observamos que oscila entre los 18 y los 20 milisegundos. Sin embargo, para los otros dos valores de 20 y 50 son habituales picos de varios -frames, llegando hasta los 5 milisegundos. Además, se aprecia cierta -inconsistencia. Sin embargo, esto no resulta un problema, pues la +frames, llegando hasta los 5 milisegundos. Además, se aprecia +cierta inconsistencia. Sin embargo, esto no resulta un problema, pues la oscilación media es de unos 3 milisegundos aproximadamente, lo cual -supone un decremento de unos 5 frames por segundo como máximo.

      +supone un decremento de unos 5 frames por segundo como +máximo.

      La naturaleza de la escena afecta en gran medida al resultado. Por mera probabilidad, cuando un rayo rebota dentro de la caja, puede salir disparado hacia muchas direcciones. Destacarían en este caso @@ -6894,32 +6912,33 @@

      Utilizando una única muestra, pero un valor de acumulación temporal -de 100 frames máximos, proporciona una imagen sin apenas ruido [59].

      +de 100 frames máximos, proporciona una imagen sin apenas ruido +[59].

      Figura 59: 1 muestra, acumulación temporal de 100 frames. A diferencia de 50, el resultado es impecable.
      -

      Subiendo los parámetros a 200 frames de acumulación temporal y 10 -muestras, se obtiene una imagen muy buena[Subiendo los parámetros a 200 frames de acumulación temporal +y 10 muestras, se obtiene una imagen muy buena[60].

      Figura 60: 10 muestras, acumulación temporal de 200 frames. +acumulación temporal de 200 frames.

      El tremendo efecto de esta técnica es debido a que actúa como normalización entre imágenes. Interpolando linealmente los resultados de -diferentes frames, con el tiempo se conseguirá una foto de lo que se -debe ver realmente, eliminando así el ruido y las luciérnagas.

      +diferentes frames, con el tiempo se conseguirá una foto de lo +que se debe ver realmente, eliminando así el ruido y las +luciérnagas.

      5.3.4 Resolución

      Como se ha mencionado en la introducción, todas las escenas @@ -7159,10 +7178,10 @@

      La respuesta es la acumulación temporal. Aunque, en esencia, la acumulación temporal es una forma de aumentar el número de muestras con respecto al tiempo, nuestra implementación utiliza interpolación para -mezclar los colores de los frames. De esta forma, se consigue el efecto -de normalización, lo cual elimina el ruido de la imagen con el tiempo. -De esta forma conseguimos equiparar la imagen de ambas versiones [Figura -66].

      +mezclar los colores de los frames. De esta forma, se consigue +el efecto de normalización, lo cual elimina el ruido de la imagen con el +tiempo. De esta forma conseguimos equiparar la imagen de ambas versiones +[Figura 66].

      Se puede observar cómo el tipo de ruido es diferente. En In One Weekend, el ruido se presenta en forma de píxeles blancos, debido a las luciérnagas generadas por el muestreo directo de la fuente de luz. En @@ -7174,8 +7193,8 @@

      +Derecha: nuestro motor (7 muestras, 15 frames +de acumulación temporal)

      Por último, subiendo el número de muestras a 1000 conseguimos una @@ -7190,7 +7209,7 @@

      Utilizando un presupuesto de 4000 milisegundos, In One Weekend es capaz de utilizar 5 muestras para la imagen, mientras que nuestro motor -podría utilizar 10 muestras y 19 frames de acumulación temporal [Figura -68].

      +podría utilizar 10 muestras y 19 frames de acumulación temporal +[Figura 68].

      \[ 20.4 \frac{\text{ms}}{\text{10 muestras}} \cdot 19 \text{ frames de temp. acum.} = 3876 \text{ ms} @@ -7224,8 +7243,8 @@

      Como podemos observar, nuestra implementación consigue un resultado abismalmente mejor en el mismo periodo de tiempo.

      Usando un valor mucho más alto de 70000 milisegundos, nuestra -implementación se puede permitir utilizar 20 muestras y 1750 frames de -acumulación temporal: \[ +implementación se puede permitir utilizar 20 muestras y 1750 +frames de acumulación temporal: \[ 40 \frac{\text{ms}}{\text{20 muestras}} \cdot 1750 \text{ frames de temp. acum.} = 70000 \text{ ms} \]

      @@ -7278,7 +7297,10 @@

      +ruido generado por las técnicas de integración de Monte Carlo, como el +muestreo por importancia. En esencia, el muestreo por importancia es una +mejor estrategia de muestreo que se adapta mejor a la distribución de +los valores del integrando.

      Una vez adquirimos el fundamento teórico, diseñamos un software que nos permitiera poner en práctica nuestros conocimientos. Escogimos la API gráfica Vulkan y un framework de Nvidia @@ -7312,9 +7334,13 @@

      (Shirley 2020a). Nos dimos cuenta de que, aunque nuestra versión -genera las muestras de forma más naïve, la rapidez con la que rinde +genera las muestras de forma más tosca, la rapidez con la que rinde consigue compensar el ruido de la imagen, produciendo así resultados más nítidos.

      +

      Por todos estos motivos, podemos afirmar que el trabajo ha sido un +éxito. Se han logrado todos los objetivos que nos propusimos de forma +satisfactoria, y gracias a la naturaleza del proyecto, hemos podido +observar los resultados gráficamente.

      6.1 Posibles mejoras

      Crear un software de este calibre es una tarea de una complejidad @@ -7328,8 +7354,8 @@

      La parte que más margen de mejora presenta es la interfaz de las fuentes de luz. En su estado actual, únicamente es posible utilizar dos tipos de fuentes externas a la escena: luces puntuales y -direccionales. Aunque se muestrean de forma directa. Además, solo puede -existir una única fuente de este tipo.

      +direccionales, las cuales se muestrean de forma directa. Además, solo +puede existir una única fuente de este tipo.

      Este diseño propicia un error relacionado con la nitidez de la imagen. Una de las ideas clave de path tracing es generar muestras de forma inteligente: dado que calcular caminos es caro, tira @@ -7340,12 +7366,13 @@

      comparativa.

      Otro tipo de interfaz que necesita una mejora sustancial es la de -materiales y objetos. En el estado actual del programa, los elementos de -la escena son cargados desde un fichero .obj. Las -propiedades del material son determinadas en el momento del impacto de -un rayo basándonos en los parámetros del archivo de materiales asociado -.mtl. Esta estrategia funciona suficientemente bien en este -caso, pues el ámbito de desarrollo es bastante reducido.

      +materiales y objetos. En el estado actual del programa, +los elementos de la escena son cargados desde un fichero +.obj. Las propiedades del material son determinadas en el +momento del impacto de un rayo basándonos en los parámetros del archivo +de materiales asociado .mtl. Esta estrategia funciona +suficientemente bien en este caso, pues el ámbito de desarrollo es +bastante reducido.

      Sin embargo, a la larga sería beneficioso reestructurar la carga y almacenamiento de los objetos. De esta forma, atacamos el problema anterior relacionado con los materiales emisivos, @@ -7357,14 +7384,13 @@

      subsurface scattering…

      Estas nuevas interfaces deberían ir acompañadas de una refactorización de la clase Engine. El -framework que escogimos, Nvidia nvpro-samples (NVIDIA 2022b), está destinado a ser -didáctico y no eficiente. Por tanto, el software presenta un alto -acoplamiento. Separar en varias clases más reducidas, como una clase -para rasterización y otra para ray tracing, sería esencial. Sin embargo, -Vulkan es una API muy compleja, por lo que requeriría de una gran -cantidad de trabajo.

      +didáctico. Por tanto, el software presenta un alto acoplamiento. Separar +en varias clases más reducidas, como una clase para rasterización y otra +para ray tracing, sería esencial. Sin embargo, Vulkan es una API muy +compleja, por lo que requeriría de una gran cantidad de trabajo.

      6.1.2 Nuevas técnicas de reducción de ruido

      @@ -7380,15 +7406,23 @@

      +

      El siguiente nivel sería implementar algunas técnicas basadas en +cadenas de Markov que vimos al final del capítulo de Monte Carlo. +Destaca el algoritmo Metropolis-Hastings (Pharr, +Jakob, and Humphreys 2016, Metropolis Light Transport). Este +tipo de técnicas resultan complejas y se escapan del ámbito +introductorio de este proyecto.

      6.1.3 Otras mejoras varias

      -

      Como es evidente, nos hemos dejado muchos detalles en el tintero. -Otro aspecto que me hubiera gustado explorar, pero que no ha sido -posible, es la capcacidad de hacer tests unitarios (lo que se conoce -como Test Driven Development). Debido a la gran velocidad de -desarrollo que ha sido requerida para sacar este proyecto adelante, -integrar test unitarios hubiera supuesto una inversión de tiempo -considerable.

      +

      Como es evidente, nos hemos dejado muchos detalles en el tintero. Un +aspecto que hubiera sido casi mandatorio desarrollar, pero que +no se ha podido, es la capcacidad de hacer tests unitarios (lo que se +conoce como Test Driven Development). Desafortunadamente los +límites de tiempo no han permitido implementar este tipo de pruebas. +Debido a la gran velocidad de desarrollo que ha sido requerida para +sacar este proyecto adelante, integrar test unitarios hubiera supuesto +una inversión de tiempo considerable.

      Esto, evidentemente, es un gran problema. Hoy en día un software profesional requiere la comprobación de que el código funciona. En este caso, se podrían hacer algunas comprobaciones como el white furnace @@ -7952,9 +7986,9 @@

      Beck et al. 2001). Apoyándonos en las herramientas ofrecidas por Github, diseñamos un sistema de requisitos mediante -issues, tanto para la memoria como para el software. Más adelante -veremos más a fondo cómo esta herramienta ha facilitado enormemente el -desarrollo.

      +issues, tanto para la memoria como para el software. Más +adelante veremos más a fondo cómo esta herramienta ha facilitado +enormemente el desarrollo.

      8.3 Presupuesto

      A la hora de desarrollar un proyecto de software, es importante @@ -8234,15 +8268,15 @@

      commit), y lo subamos a Github (acción de -push), se ejecutará un denominado Action que -operará sobre nuestros archivos.

      -

      Tendremos dos tipos de Actions: uno que se encarga de +como un commit), y lo subamos a Github (acción de +push), se ejecutará un denominado action que operará +sobre nuestros archivos.

      +

      Tendremos dos tipos de Actions: uno que se encarga de compilar la web, y otro el PDF. En esencia, operan de la siguiente manera:

      1. Comprueba si se ha modificado algún fichero .md en el -último commit subido. Si no es el caso, para.
      2. +último commit subido. Si no es el caso, para.
      3. Si sí se ha modificado, accede a la carpeta del repositorio y compila la documentación mediante pandoc.
          @@ -8250,7 +8284,7 @@

        1. El PDF se crea en docs/TFG.pdf
      4. -
      5. Commitea los archivos y termina.
      6. +
      7. Añade los archivos al commit y termina.
      @@ -8298,11 +8332,12 @@

      ./Dockerfile.

      8.6.2 Issues y Github Projects

      -

      Las tareas pendientes se gestionan mediante issues. Cada vez que se -tenga un objetivo particular para el desarrollo, se anota un issue. -Cuando se genere un commit que avance dicha tarea, se etiqueta con el -número correspondiente al issue. De esta forma, todas las confirmaciones -relacionadas con la tarea quedan recogidas en la página web.

      +

      Las tareas pendientes se gestionan mediante issues. Cada vez +que se tenga un objetivo particular para el desarrollo, se anota un +issue. Cuando se genere un commit que avance dicha tarea, se +etiqueta con el número correspondiente al issue. De esta forma, todas +las confirmaciones relacionadas con la tarea quedan recogidas en la +página web.

      Esto permite una gestión muy eficiente de los principales problemas y objetivos pendientes de la aplicación.

      @@ -8310,19 +8345,19 @@

      +issues, día 16 de abril de 2022

      -

      Los issues se agrupan en milestones, o productos mínimamente -viables. Estos issues suelen estar relacionados con algún apartado -importante del desarrollo.

      +

      Los issues se agrupan en milestones, o productos +mínimamente viables. Estos issues suelen estar relacionados con +algún apartado importante del desarrollo.

      Figura 90: Los milestones agrupan una serie de issues relacionados con un punto clave del desarrollo +milestones agrupan una serie de issues relacionados +con un punto clave del desarrollo

      De esta forma, podemos ver todo lo que queda pendiente para la fecha @@ -8335,22 +8370,22 @@

      +los issues y les asigna prioridades

      Una de las alternativas que se planteó al inicio fue Linear (Linear 2022), una -aplicación de gestión de issues similar a Projects. Sin embargo, la -conveniencia de tener Projects integrado en Github supuso un punto a -favor para este gestor. De todas formas, el equipo de desarrollo se -compone de una persona, así que no hace falta complicar excesivamente el -workflow.

      +aplicación de gestión de issues similar a Projects. Sin +embargo, la conveniencia de tener Projects integrado en Github supuso un +punto a favor para este gestor. De todas formas, el equipo de desarrollo +se compone de una persona, así que no hace falta complicar excesivamente +el workflow.

      El desarrollo general de la documentación no ha seguido este sistema -de issues, pues está sujeta a cambios constantes y cada commit está -marcado con [:notebook:]. No obstante, ciertos problemas -relacionados con ella, como puede ser el formato de entrega, sí que -quedan recogidos como un issue.

      +de issues, pues está sujeta a cambios constantes y cada +commit está marcado con [:notebook:]. No obstante, +ciertos problemas relacionados con ella, como puede ser el formato de +entrega, sí que quedan recogidos como un issue.

      Finalmente, cuando se produce un cambio significativo en la aplicación (como puede ser una refactorización, una implementación considerablemente más compleja…) se genera una nueva rama. Cuando se ha @@ -8360,10 +8395,10 @@

      8.6.3 Estilo de commits

      Una de los detalles que has podido apreciar si has entrado al -repositorio es un estilo de commit un tanto inusual. Aunque parece un -detalle de lo más insustancial, añadir emojis a los mensajes de commits -añade un toque particular al repositorio, y permite identificar -rápidamente el tipo de cambio.

      +repositorio es un estilo de commit un tanto inusual. Aunque +parece un detalle de lo más insustancial, añadir emojis a los mensajes +de commits añade un toque particular al repositorio, y permite +identificar rápidamente el tipo de cambio.

      Cada uno tiene un significado particular. En esta tabla se recogen sus significados:

      @@ -8371,8 +8406,8 @@

      +permiten reconocer el objetivo de cada commit. Esta tabla +recoge el significado de cada uno

      Bibliografía

      class="footnote-back" role="doc-backlink">↩︎

    • A no ser que se utilicen motion vectors, los cuales codifican información sobre el movimiento de un -objeto al pasar de un frame a otro. Estos permiten implementar técnicas -como temporal antialiasing, los cuales veremos en una sección -posterior.frame a otro. Estos permiten implementar +técnicas como temporal antialiasing, los cuales veremos en una +sección posterior.↩︎