Plugin de Blender para exportación de materiales a Unity Blender to Unity Material Export Plug-in Trabajo de Fin de Grado Curso 2023–2024 Autoras Miriam Martín Sánchez, Paula Morillas Alonso, Elisa Todd Rodríguez Directores Pedro Pablo Gómez Martín Guillermo Jiménez Díaz Grado en Desarrollo de Videojuegos Facultad de Informática Universidad Complutense de Madrid Plugin de Blender para exportación de materiales a Unity Blender to Unity Material Export Plug-in Trabajo de Fin de Grado en Desarrollo de Videojuegos Autoras Miriam Martín Sánchez, Paula Morillas Alonso, Elisa Todd Rodríguez Directores Pedro Pablo Gómez Martín Guillermo Jiménez Díaz Convocatoria: Junio 2024 Grado en Desarrollo de Videojuegos Facultad de Informática Universidad Complutense de Madrid 27 de MAYO de 2024 Dedicatoria A mis padres por su apoyo incondicional, su cariño y su amor. A mi hermano mellizo, Diego, por estar siempre a mi lado. A mi gato, Oreinchi, por acompañarme durante las largas noches de trabajo en el TFG. Y a José Daniel, por mostrar siempre curiosidad e interés en este proyecto. -Miriam A todas las personas con ganas de aprender que no han podido llegar a este punto en sus carreras y a los que tienen ganas de cambiar el mundo, pero no tienen la oportunidad. Que este trabajo sea vuestro también. A mi familia y a todos mis amigos, pero en especial a Águeda, por ser mi mayor apoyo estos años y recorrer conmigo este camino. -Paula To my dad, whose memory is present in everything I do. I hope I have made you proud. A mi madre, por brindarme su apoyo durante todos estos años. Y a mí misma, por no rendirme nunca y siempre luchar por lo que quiero. -Eli v Agradecimientos Gracias a Guille, por habernos visto crecer, por enseñarnos y acompañarnos todos estos años; por siempre hacernos ver el lado positivo de las cosas y recordarnos que no hay trabajo sin descanso. Gracias a Pedro Pablo, por compartir sus conocimientos con nosotras, por hacer que cualquier tema se vuelva interesante; por contagiarnos el interés y la pasión por todo lo que enseña. Gracias a todas las personas que han contribuido al desarrollo de este proyec- to participando en nuestras encuestas y haciendo pruebas de la herramienta. Nos ayudáis a ver nuestros fallos y nos empujáis a hacerlo cada vez mejor. vii Resumen Plugin de Blender para exportación de materiales a Unity Uno de los desafíos recurrentes en el desarrollo de videojuegos es la transición fluida entre distintas herramientas y motores de renderizado. A menudo, los artistas y modeladores crean sus diseños en programas como Blender, para más tarde expor- tarlos e integrarlos en motores de desarrollo como Unity. Sin embargo, la falta de compatibilidad entre los formatos utilizados por estos programas genera problemas de integración que comprometen la calidad del trabajo original, además de suponer una carga de trabajo adicional. El objetivo de este trabajo es facilitar el proceso de integración de elementos generados en Blender, concretamente los materiales, en el entorno de Unity. Se busca no solo eso, sino también generar un resultado que mantenga la calidad y versatilidad del material original, asegurando una copia exacta que sea compatible con la segunda plataforma. La implementación del conversor se basa en técnicas de procesamiento de datos y algoritmos de generación de archivos personalizados mediante plantillas. Además, se ha diseñado de manera que se puede integrar sencillamente en el flujo de trabajo existente de los desarrolladores, proporcionando una solución práctica y eficaz para la interoperabilidad entre Blender y Unity en el desarrollo de videojuegos. Palabras clave Desarrollo de Videojuegos, Integración, Conversor Automático, Blender, Unity, Ma- teriales, Shaders. ix Abstract Blender to Unity Material Export Plug-in One of the recurring challenges in game development is the smooth transition between different tools and rendering engines. Often, artists and modelers create their designs in programs like Blender, later exporting and integrating them into development engines like Unity. However, the lack of compatibility between the for- mats used by these programs creates integration issues that compromise the quality of the original work, in addition to posing a greater workload. The aim of this project is to streamline the integration process of elements gen- erated in Blender, specifically materials, into the Unity environment. The goal is not only to achieve this, but also to generate a result that maintains the quality and versatility of the original material, ensuring an exact copy that is compatible with the second platform. The implementation of the converter is based on data processing techniques and custom file generation algorithms. Additionally, it has been designed to be seam- lessly integrated into the existing workflow of developers, providing a practical and effective solution for interoperability between Blender and Unity in game develop- ment. Keywords Video Game Development, Integration, Automatic Converter, Blender, Unity, Ma- terials, Shaders. xi Índice Dedicatoria v Agradecimientos vii Resumen ix Abstract xi 1. Introducción 1 1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3. Plan de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.4. Estructura de la memoria . . . . . . . . . . . . . . . . . . . . . . . . 3 2. Introducción al Renderizado 5 2.1. Elementos de una escena . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.1. Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.2. Cámara . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.1.3. Iluminación . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.1.4. Viewport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2. Formas de renderizado . . . . . . . . . . . . . . . . . . . . . . . . . . 12 xiii 2.2.1. Basado en luz . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.2. Basado en objetos . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3. Materiales e interacción con la luz . . . . . . . . . . . . . . . . . . . . 20 2.3.1. Normales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.3.2. Sombreado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.3. Modelo de iluminación . . . . . . . . . . . . . . . . . . . . . . 23 2.3.4. Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3.5. Renderizado basado en físicas y función de distribución de dispersión bidireccional . . . . . . . . . . . . . . . . . . . . . . 26 2.4. Pipeline gráfico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.4.1. Shader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.4.2. Programación de shaders . . . . . . . . . . . . . . . . . . . . . 33 2.4.3. Pipeline gráfico deferred . . . . . . . . . . . . . . . . . . . . . 35 2.4.4. Ventajas y desventajas de deferred y forward rendering . . . . 35 3. Blender 39 3.1. Objetos en Blender . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.2. Materiales en Blender . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.2.1. Creación de materiales . . . . . . . . . . . . . . . . . . . . . . 41 3.2.2. Nodo Principled BSDF . . . . . . . . . . . . . . . . . . . . . . 44 3.3. Motores de Renderizado . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.3.1. Cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.3.2. Eevee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.3.3. Similitudes y diferencias entre Eevee y Cycles . . . . . . . . . 47 3.4. Add-ons en Blender . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4. Unity 51 4.1. Renderizado en Unity . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.1.1. Universal Render Pipeline . . . . . . . . . . . . . . . . . . . . 52 4.1.2. High Definition Render Pipeline . . . . . . . . . . . . . . . . . 53 4.2. Objetos en Unity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.3. Materiales en Unity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.3.1. Shaders en Unity . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.3.2. Modelos de shaders predefinidos . . . . . . . . . . . . . . . . . 57 4.3.3. Propiedades de los materiales en Unity . . . . . . . . . . . . . 58 4.4. Archivos .meta y GUID . . . . . . . . . . . . . . . . . . . . . . . . . 61 5. Diseño de la herramienta 63 5.1. Estado actual de la exportación de materiales de Blender a Unity . . 63 5.2. Descripción funcional de la herramienta . . . . . . . . . . . . . . . . . 67 5.2.1. Uso de los nodos de Blender . . . . . . . . . . . . . . . . . . . 69 6. Implementación del add-on 73 6.1. Generación del archivo .shader . . . . . . . . . . . . . . . . . . . . . . 75 6.1.1. Plantilla .shader . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.1.2. Recorrido de los nodos . . . . . . . . . . . . . . . . . . . . . . 78 6.1.3. Instanciación de los nodos en la plantilla . . . . . . . . . . . . 79 6.1.4. Transparencia y Corrección Gamma . . . . . . . . . . . . . . . 92 6.2. Generación de archivos de imagen . . . . . . . . . . . . . . . . . . . . 97 6.3. Generación del archivo .mat . . . . . . . . . . . . . . . . . . . . . . . 98 6.4. Generación del archivo .fbx . . . . . . . . . . . . . . . . . . . . . . . . 101 6.5. Generación del archivo .prefab . . . . . . . . . . . . . . . . . . . . . . 101 6.6. Empaquetado del Add-on . . . . . . . . . . . . . . . . . . . . . . . . 102 6.7. Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 7. Pruebas de evaluación con usuarios 107 7.1. Objetivo de las pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . 107 7.2. Requisitos para los participantes . . . . . . . . . . . . . . . . . . . . . 108 7.3. Métodos de prueba . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 7.4. Pruebas realizadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 8. Conclusiones y Trabajo Futuro 113 8.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 8.2. Trabajo Futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 9. Contribuciones Personales 117 Bibliografía 123 10.Introduction 125 10.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 10.2. Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 10.3. Work Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 10.4. Document Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 11.Conclusions and Future Work 129 11.1. Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 11.2. Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 A. Blender to Unity Material Export Add-On Installation and Usage Guide 133 A.1. Installation Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 A.2. Using the Add-On . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 Índice de figuras 2.1. Malla de triángulos del modelo conocido como “Conejo de Stanford”. . 6 2.2. Local space. Fuente: OpenGL4 . . . . . . . . . . . . . . . . . . . . . . 7 2.3. World space. Fuente: OpenGL4 . . . . . . . . . . . . . . . . . . . . . 7 2.4. Planos cercano, lejano y el frustum de la cámara. Fuente: LearnOpenGL 8 2.5. Camara ortográfica. Fuente: LearnOpenGL,CatLike . . . . . . . . . . 9 2.6. Camara en perspectiva. Fuente: CatLike, LearnOpenGL . . . . . . . . 9 2.7. Render con proyeccion ortografica y perspectiva. Fuente: Unity . . . . 9 2.8. Representación esquemática de la propagación de la luz puntual. Fuente: Borromeo (2020) . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.9. Representación esquemática de la propagación de la luz direccional. Fuente: Borromeo (2020) . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.10. Representación esquemática de la propagación de la luz focalizada. Fuente: Borromeo (2020) . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.11. RayTracing. Fuente: scratchapixel . . . . . . . . . . . . . . . . . . . . 14 2.12. Reflexión y refracción en ray tracing. Fuente: scratchapixel . . . . . . 15 2.13. Rasterization. Fuente: scratchapixel . . . . . . . . . . . . . . . . . . . 16 2.14. Problema de la profundidad en el algoritmo del pintor. Fuente: Wi- kipedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.15. Representación 3D del Z-Buffer. Fuente: Hecktor Docs . . . . . . . . 18 2.16. Representación de la aplicación de una fórmula típica de mezclado. Fuente: Han (2018) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 xvii https://www.mbsoftworks.sk/tutorials/opengl4/004-entering-third-dimension/ https://www.mbsoftworks.sk/tutorials/opengl4/004-entering-third-dimension/ https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling https://learnopengl.com/Getting-started/Coordinate-Systems https://catlikecoding.com/unity/tutorials/rendering/part-1/ https://catlikecoding.com/unity/tutorials/rendering/part-1/ https://learnopengl.com/Getting-started/Coordinate-Systems https://discussions.unity.com/t/comparing-orthographic-and-perspective-cameras/169520 https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing/implementing-the-raytracing-algorithm.html https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-overview/light-transport-ray-tracing-whitted.html https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/overview-rasterization-algorithm.html https://es.wikipedia.org/wiki/Algoritmo_del_pintor https://es.wikipedia.org/wiki/Algoritmo_del_pintor https://docs.hektorprofe.net/graficos-3d/24-profundidad-con-z-buffer/ 2.18. Comparativa de los modelos de sombreado aplicados sobre un mismo modelo. Fuente: (Gambetta, 2021) . . . . . . . . . . . . . . . . . . . . 22 2.19. Componentes esenciales para representar el color de un objeto. Fuen- te: Wikipedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.20. Suma de las componentes y aplicación del modelo Phong. Fuente: Wikipedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.22. Mapeo de una textura en una malla. Fuente: HaroldSerrano . . . . . 25 2.23. UVs de una malla . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.24. BSDF = BRDF + BTDF. Fuente: Wikipedia . . . . . . . . . . . . . 27 2.25. Ejemplo del proceso de renderizado que realiza el render pipeline (Akenine-Moller et al., 2018). . . . . . . . . . . . . . . . . . . . . . . 28 2.26. Transformaciones hasta llegar a una representación 2D.Fuente:LearnOpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.27. Etapas del pipeline de renderizado. Las etapas encuadradas de color amarillo representan las partes del pipeline que se pueden personalizar usando shaders. Fuente: Intro Vulkan . . . . . . . . . . . . . . . . . . 31 2.28. Ejemplo de contenido del G-buffer. Fuente: Borromeo (2020) . . . . . 35 2.29. La combinación de las tres luces que han sido aplicadas al G-Buffer en el paso mostrado anteriormente. Fuente: Borromeo (2020) . . . . . 36 3.1. Shaders que componen los materiales de Blender. Fuente: Manual de Blender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.2. Vista de la interfaz de Blender. . . . . . . . . . . . . . . . . . . . . . 42 3.3. Añadir o crear un material en Blender. . . . . . . . . . . . . . . . . . 42 3.4. Pestaña de edición de materiales. . . . . . . . . . . . . . . . . . . . . 43 3.5. Conexiones entre los nodos de un material. . . . . . . . . . . . . . . . 43 3.6. Nodo Principled BSDF . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.7. Materiales realistas creados haciendo uso del nodo Principled BSDF. Fuente: Sbalu (2023) . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.8. Intensidad de emisión de 0.0 a 10.0. Fuente: Manual de Blender . . . 45 3.9. Ejemplos de dos renderizados diferentes de una misma escena en Blen- der. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 https://en.wikipedia.org/wiki/Phong_reflection_model https://en.wikipedia.org/wiki/Phong_reflection_model https://www.haroldserrano.com/blog/applying-textures-to-3d-objects-in-metal https://en.wikipedia.org/wiki/Bidirectional_scattering_distribution_function https://learnopengl.com/Getting-started/Coordinate-Systems https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Introduction https://docs.blender.org/manual/en/latest/render/materials/introduction.html####physically-based-shading https://docs.blender.org/manual/en/latest/render/materials/introduction.html####physically-based-shading https://docs.blender.org/manual/en/latest/render/shader_nodes/shader/principled.html 4.1. Bucle de renderizado en URP. Fuente: Documentación de Unity . . . 53 4.2. Un GameObject de Unity con varios componentes asociados. Fuente: Documentación de Unity . . . . . . . . . . . . . . . . . . . . . . . . . 54 5.1. Objeto con un material asociado en Blender. . . . . . . . . . . . . . . 64 5.2. Material de la figura 5.1, visto por nodos. . . . . . . . . . . . . . . . . 65 5.3. Resultado de la primera exportación en Unity. . . . . . . . . . . . . . 66 5.4. Interfaz de Blender donde se realiza el proceso de baking. . . . . . . . 66 5.5. Resultado tras el proceso de baking en Unity. . . . . . . . . . . . . . 67 5.6. Interfaz del Add-on en Blender . . . . . . . . . . . . . . . . . . . . . 69 5.7. Respuestas para la pregunta “Seleccione cuáles de los siguientes tipos de materiales utiliza con frecuencia en sus proyectos” en la encuesta realizada. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.8. Respuestas para la pregunta “Seleccione cuáles de los siguientes ti- pos de nodos de material utiliza más frecuentemente” en la encuesta realizada. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6.1. Depth First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 6.2. Nodos Value y RGB en Blender . . . . . . . . . . . . . . . . . . . . . 81 6.3. Correspondencia entre parámetros de Principled BSDF en Blender y parámetros de SurfaceData en Unity . . . . . . . . . . . . . . . . . . 82 6.4. Nodo Mix en sus diferentes configuraciones. Fuente: Manual de Blender 84 6.5. Nodo Add Shader. Fuente: Manual de Blender . . . . . . . . . . . . . 85 6.6. Nodo Mix Shader. Fuente: Manual de Blender . . . . . . . . . . . . . 85 6.7. Nodo Shader to RGB. . . . . . . . . . . . . . . . . . . . . . . . . . . 86 6.8. Nodo Texture Coordinate. . . . . . . . . . . . . . . . . . . . . . . . . 87 6.9. Nodo Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 6.10. Nodo Image Texture . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 6.11. Nodo Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 6.12. Nodo Color Ramp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.13. Nodo Voronoi Texture . . . . . . . . . . . . . . . . . . . . . . . . . . 91 https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/manual/rendering-in-universalrp.html https://docs.unity3d.com/Manual/GameObjects.html https://docs.blender.org/manual/en/3.6/modeling/geometry_nodes/utilities/math/mix.html https://docs.blender.org/manual/id/3.6/render/shader_nodes/shader/add.html https://docs.blender.org/manual/id/3.6/render/shader_nodes/shader/mix.html 6.14. Voronoi Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 6.15. Nodo Normal Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 6.17. Representación de dos esferas de diferentes colores y con transparencia en Unity utilizando la operación de mezcla “Add” . . . . . . . . . . . 94 6.18. Representación de dos esferas de diferentes colores y con transparencia en Unity utilizando la operación de mezcla “Substract” en la esfera roja, y “Add” en la esfera azul . . . . . . . . . . . . . . . . . . . . . . 95 6.19. Visualización del cambio producido en el material al modificar el um- bral de Alpha Clip. Fuente: Daniel Ilett . . . . . . . . . . . . . . . . 96 6.20. Comparativa de valores RGB en los diferentes espacios de trabajo. . . 96 6.21. En un escenario común de gamma = 2.2, así es como el monitor realmente muestra las intensidades de color de tu juego (curva verde). La línea roja punteada muestra cómo un monitor lineal mostraría las mismas intensidades. Fuente: David Davidović . . . . . . . . . . . . . 97 6.22. Archivos generados por la herramienta para un caso de ejemplo. . . . 98 6.23. Objeto exportado a Unity, junto con sus archivos en la vista de editor.102 6.24. Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. . . . . . . . . . . . . . . 103 6.25. Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. . . . . . . . . . . . . . . 103 6.26. Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. . . . . . . . . . . . . . . 104 6.27. Modelo exportado con la herramienta. . . . . . . . . . . . . . . . . . 104 6.28. Modelo exportado con la herramienta. . . . . . . . . . . . . . . . . . 105 6.29. Modelo exportado con la herramienta. . . . . . . . . . . . . . . . . . 106 6.30. Modelo exportado con la herramienta. . . . . . . . . . . . . . . . . . 106 7.1. Valoración de los usuarios de su conocimiento de Blender. . . . . . . . 109 7.2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 7.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 7.4. Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. . . . . . . . . . . . . . . . . . . . . . . . . . . 111 https://danielilett.com/ https://gamedevelopment.tutsplus.com/es/correccion-gamma-y-por-que-es-importante--gamedev-14466a 7.5. Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. . . . . . . . . . . . . . . . . . . . . . . . . . . 111 7.6. Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. . . . . . . . . . . . . . . . . . . . . . . . . . . 112 7.7. Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Capı́tulo 1 Introducción “If you can’t give me poetry, can’t you give me poetical science?” — Ada Lovelace 1.1. Motivación El proceso de creación de contenido artístico para videojuegos implica la colabo- ración entre diferentes sectores profesionales en los que se encuentran tanto artistas como programadores. Sin embargo, esta colaboración puede encontrarse con desafíos significativos debido a las diferencias en las herramientas y los formatos de archi- vo utilizados en cada sector. Presenta una particular dificultad la transferencia de materiales desde el software de modelado Blender a los motores de juego como Unity. La motivación de este proyecto consiste en abordar este problema y mejorar el flujo de trabajo para los artistas y desarrolladores de videojuegos. Actualmente, el proceso de exportar materiales de Blender a Unity en su mayoría implica una pérdida de información y calidad además de una ruptura visual entre lo creado por el artista, y lo mostrado en el producto final. Igualmente, este proceso consume una cantidad elevada de tiempo y es tedioso. La decisión de desarrollar una herramienta para Blender que permita exportar materiales a un formato compatible con Unity parte del deseo de facilitar la transfe- rencia de contenido entre las dos plataformas mencionadas. Esta nueva herramienta permitirá a los artistas mantener el aspecto de sus materiales en el producto final, al mismo tiempo que les facilitará el proceso de exportación. Esto no solo ayudará a mejorar la calidad visual, sino que también optimizará el flujo de trabajo de los artistas al reducir la necesidad de hacer ajustes manuales. Por otro lado, también optimizará el trabajo de los programadores, quienes obtendrán el contenido en un formato especializado y compatible con su plataforma de trabajo. 1 2 Capítulo 1. Introducción Esta herramienta tiene potencial para mejorar la comunicación entre artistas y desarrolladores. Al asegurar que los materiales creados en Blender se transfieran de manera precisa y eficiente a Unity, se reducirá la necesidad de hacer correcciones y ajustes por parte de los programadores, reduciendo su carga de trabajo y su nivel de frustración. Esto permitirá una colaboración más fluida y eficiente entre ambos grupos, ya que podrán centrarse en su trabajo principal en lugar de ocuparse de problemas de compatibilidad y transferencia de archivos. 1.2. Objetivos El objetivo del proyecto es diseñar e implementar una herramienta ejecutable en la plataforma Blender que sea capaz de exportar objetos de una escena de Blender a una escena en Unity, incluyendo los materiales asociados a dichos objetos, obteniendo así materiales y shaders compatibles con Unity. Se pretende alcanzar un punto en el que la herramienta sea capaz de exportar los materiales más comúnmente usados en Blender, de manera que irá acompañada de un manual de usuario donde indique claramente los requisitos y limitaciones de la misma. Para poder conseguir este objetivo será necesario lograr una serie de objetivos intermedios, comenzando por el estudio del proceso de renderizado para videojuegos, y cómo se aplica este en las herramientas implicadas en nuestro proyecto, Blender y Unity. También será necesario el estudio de los materiales más comúnmente utiliza- dos en Blender y cómo interpretarlos adecuadamente en Unity. Finalmente, gracias a dichos estudios podremos desarrollar la herramienta como add-on para Blender, realizando una evaluación de uso de la misma. 1.3. Plan de trabajo Para el desarrollo de este trabajo será empleada la metodología ágil de desarrollo Scrum, de manera flexible para adaptarse mejor a las necesidades del equipo. De esta manera, se dividirán las tareas en historias de usuario asignadas a las integrantes del equipo en función de la disponibilidad o conocimiento previo de la materia. Para seguir adecuadamente el progreso, se realizarán reuniones semanales, de manera similar a cómo se han ido haciendo durante asignaturas como Proyectos. Estas reuniones tendrán el objetivo principal de actualizar al equipo con los avances conseguidos, y reorganizar las tareas si fuese necesario. Las reuniones con los tutores se efectuarán como norma general cada dos sema- nas, con excepciones en semanas en las que el progreso haya sido bajo. Se utilizarán para actualizar a los tutores con los avances conseguidos, realizar consultas técnicas 1.4. Estructura de la memoria 3 sobre áreas específicas, y recibir consejos para dirigir el trabajo por un buen camino. El primer punto a investigar por el equipo será el entorno ya existente en las aplicaciones en las que vamos a trabajar: Blender y Unity. Uno de los primeros pasos fundamentales será hacer una investigación profunda sobre la tecnología de renderizado existente en estas plataformas, la forma en que gestionan los materiales, el funcionamiento de la iluminación y otros puntos clave para basar las decisiones tomadas más adelante en unos cimientos de conocimiento más sólidos. Una vez hecha esta investigación, se implementará la herramienta de manera que resulte en un add-on para el usuario de Blender, que pueda ejecutar fácilmente desde su ordenador. Esta herramienta será versátil y adaptable a la mayoría de materiales que el usuario pueda crear en Blender. Puesto que Blender cuenta con más de cien nodos de material diferentes, cada uno con diversos modos de configuración y variaciones, para acotar el alcance del proyecto se realizará una investigación con el objetivo de conocer las características más utilizadas por los usuarios, y así implementarlas en la herramienta. Dicho desarrollo se llevará a cabo en el repositorio GitHub disponible en la si- guiente URL: https://github.com/TFG-MEP/Blender_to_Unity_Material_Export_ Plug-in. Finalmente, puesto que la intención del proyecto es proporcionar una herramienta útil para los desarrolladores, se realizarán pruebas con usuarios para asegurar el correcto funcionamiento de la misma. El objetivo de las pruebas será conocer las opiniones de los usuarios sobre el uso de la herramienta y localizar errores, para solucionarlos y ofrecer una mejor experiencia a los potenciales usuarios. 1.4. Estructura de la memoria En el capítulo 2 Introducción al Renderizado se describen en detalle los procesos necesarios para comprender este proyecto en profundidad. En los capítulos 3 y 4 se describe en detalle la investigación previa a la creación de la herramienta relacionada con los programas utilizados al igual que se justifican las decisiones tomadas para su correcta implementación. En el capítulo 5 Diseño de la herramienta se plantea la situación actual de la exportación de materiales de Blender a Unity para, a continuación, exponer una descripción del funcionamiento de la misma a nivel funcional y de diseño. Para res- paldar las decisiones de diseño tomadas, este capítulo incluye una encuesta realizada sobre el uso de materiales en Blender. El el capítulo 6 Implementación del add-on se profundiza en el proceso de im- plementación de la herramienta tal y como se ha diseñado en el capítulo anterior. Se expone el proceso de exportación que realiza la herramienta y se muestran una https://github.com/TFG-MEP/Blender_to_Unity_Material_Export_Plug-in https://github.com/TFG-MEP/Blender_to_Unity_Material_Export_Plug-in 4 Capítulo 1. Introducción serie de ejemplos para visualizar el resultado obtenido. En el capítulo 7 Pruebas de evaluación con usuarios se describen los diferentes métodos de prueba, así como las propias pruebas, que han permitido valorar el uso de la herramienta y comprobar su correcto funcionamiento al ser utilizada por diferentes usuarios. Capı́tulo 2 Introducción al Renderizado El renderizado en el contexto de los gráficos por ordenador, también conocido como visualización, es el proceso que consiste en transformar la descripción de una escena en 3D en una imagen 2D que se presenta en la pantalla de un dispositivo. Este proceso implica la aplicación de diversos efectos visuales, como sombras, reflejos y texturas, para lograr una representación realista de la escena. En este capítulo se procede a hacer una explicación detallada del proceso de renderizado. Se abordan los elementos fundamentales de una escena, y se explican las diversas formas de generación de imágenes, tanto basadas en luz como en ob- jetos. También se analizan los materiales y su interacción con la luz, el modelo de sombreado y los parámetros típicos de materiales, además de poner especial énfasis en el pipeline gráfico y el concepto y uso de shaders. El objetivo de este capítulo es proporcionar una descripción exhaustiva de cómo se transforma una escena 3D en una imagen 2D en la pantalla de un dispositivo. Esto ayudará al correcto plante- amiento del proyecto además de facilitar la comprensión de diferentes problemas y abordar múltiples aspectos necesarios para el desarrollo del proyecto. 2.1. Elementos de una escena Cualquier escena 3D que queramos convertir a una imagen 2D tiene una serie de elementos, que se describen a continuación. 2.1.1. Objetos Una parte clave de los programas de gráficos es el uso de modelos geométricos 3D. Estos modelos describen objetos tridimensionales utilizando primitivas mate- máticas como esferas, cubos, conos y polígonos. El tipo de modelo más utilizado 5 6 Capítulo 2. Introducción al Renderizado está compuesto por triángulos con vértices compartidos, que a menudo se denomi- na malla de triángulos. Estas mallas a veces son generadas por artistas utilizando programas de modelado tridimensional como pueden ser Blender o Maya (Shirley et al., 2009). Para que estos objetos sean visibles es necesario el uso de materiales, que se pue- den entender como una capa que envuelve estos modelos. Los materiales determinan cómo interactúa la luz con la superficie de los objetos, lo que afecta la apariencia visual de los mismos. Pueden simular propiedades como el color, la textura, la opa- cidad, la reflexión y la refracción, entre otras. Figura 2.1: Malla de triángulos del modelo conocido como “Conejo de Stanford”. Estos objetos tienen una posición en la escena, con coordenadas de mundo y coordenadas locales. Coordenadas locales (Object space/Model Space): Las coordenadas lo- cales son relativas al punto de origen del objeto, es decir, el punto que se usa para situar el objeto en la escena o donde “comienza” el objeto. Este origen se sitúa en una posición significativa del objeto, como puede ser la base de un jarrón o el centro de un cubo, como se puede observar en la figura 2.2. Coordenadas mundo (World space): Una vez que hemos establecido las coordenadas locales para nuestros objetos, como no queremos que todos los objetos que importemos a la escena permanezcan en el origen (0,0,0), necesi- tamos asignar una posición específica a cada objeto para situarlos dentro del mundo. Por ello, la propia escena tiene un origen de coordenadas, y todos los objetos dentro de ella se sitúan de manera relativa a esta posición central. Las coordenadas en el espacio de mundo son precisamente lo que su nombre indica: las coordenadas de todos los vértices en relación con un mundo global. La imagen 2.3 ilustra las coordenadas del mundo de los vértices del modelo al ser desplazado la posición (25,0,0). Se puede ver cómo la posición de los vértices es relativa al origen de coordenadas del mundo. 2.1. Elementos de una escena 7 Figura 2.2: Local space. Fuente: OpenGL4 Figura 2.3: World space. Fuente: OpenGL4 Existen varios formatos de archivo ampliamente soportados para almacenar mo- delos, siendo OBJ y FBX algunas de las opciones más populares. OBJ: Es un formato sencillo que almacena la información en formato ASCII, de modo que puede ser leído y editado con un editor de texto estándar. Alma- cena únicamente información sobre la geometría, no admite animaciones y el soporte para describir las propiedades de material de los objetos es limitado. FBX: Desarrollado por Autodesk, es ampliamente utilizado en la industria del entretenimiento, especialmente en la creación de contenido para videojue- gos, películas y animaciones. Con capacidad para almacenar datos complejos de escenas 3D, como geometría, luces, cámaras y otros elementos, FBX es co- nocido por su versatilidad. Los archivos FBX pueden estar en formato ASCII o binario y preservan la jerarquía de objetos de la escena, lo que facilita su organización (Dijkman, 2021). 2.1.2. Cámara Puesto que la pantalla del espectador es bidimensional, se necesita capturar una vista 3D de una escena y convertirla en una imagen que pueda ser visualizada en la pantalla. Esto se logra mediante el uso de cámaras. https://www.mbsoftworks.sk/tutorials/opengl4/004-entering-third-dimension/ https://www.mbsoftworks.sk/tutorials/opengl4/004-entering-third-dimension/ 8 Capítulo 2. Introducción al Renderizado La cámara es un dispositivo virtual que registra y muestra una escena. Esta cámara tiene sus propias coordenadas tanto globales como locales, lo que incluye su posición y rotación. Esto le permite ser transformada y colocada en cualquier punto de la escena para capturar y mostrar cualquier parte de la misma. Los modelos u objetos que detecta la cámara serán procesados visualmente, pero solamente incluirá aquellos que se encuentren dentro de un rango definido por dos planos imaginarios: el plano de recorte cercano y el plano de recorte lejano. Estos dos planos delimitan un área tridimensional, que es el volumen de vista, véase en la figura 2.4. Estos planos se sitúan a distancias específicas de la cámara a lo largo de su línea de visión y determinan qué objetos serán procesados visualmente si estos se encuentran ubicados entre estos dos planos (Akenine-Moller et al., 2018). Los demás objetos que se hallen fuera del campo de visión de la cámara se eliminarán, mediante el proceso conocido como frustum culling. La cámara cuenta con un ángulo de apertura, también llamado “FOV” (Field of view). Este parámetro es similar a la apertura del objetivo de una cámara: cuanto más grande sea este ángulo mayor parte de la escena veremos en la imagen final. Se producirá un efecto similar al de hacer o deshacer zoom. Figura 2.4: Planos cercano, lejano y el frustum de la cámara. Fuente: LearnOpenGL Existen dos tipos principales de proyección utilizados en el renderizado de esce- nas: Ortográfica: Consiste en representar los objetos de la escena mediante una proyección ortogonal sobre el plano cercano, el volumen de vista de la cámara es un prisma cuadrangular. Es decir, la proyección ortográfica captura la escena sin tener en cuenta la perspectiva, lo que resulta en una representación más plana y sin distorsiones de los objetos en la pantalla (figura 2.5). Perspectiva: Representación visual que simula cómo se percibe una escena en la vida real. En esta representación, los objetos que están más alejados de la cámara parecen más pequeños en comparación con los objetos que están más cerca, lo que crea una sensación de profundidad y distancia en la imagen final (figura 2.6). Para delimitar esta perspectiva, se utiliza el frustum, que es una región tridimensional en forma de pirámide cuadrangular truncada. El frustum define los límites del campo visual, mostrando el volumen de vista https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling 2.1. Elementos de una escena 9 Figura 2.5: Camara ortográfica. Fuente: LearnOpenGL,CatLike que abarca desde el plano cercano hasta el plano lejano. En la imagen 2.4 se puede observar el frustum de manera clara. Figura 2.6: Camara en perspectiva. Fuente: CatLike, LearnOpenGL Figura 2.7: Render con proyeccion ortografica y perspectiva. Fuente: Unity 2.1.2.1. Camera Space/View Space En el proceso de renderizado de una escena, para lograr una representación visual eficiente, se emplea la transformación de los modelos de la escena de world space al espacio de la cámara, también conocido como camera space. https://learnopengl.com/Getting-started/Coordinate-Systems https://catlikecoding.com/unity/tutorials/rendering/part-1/ https://catlikecoding.com/unity/tutorials/rendering/part-1/ https://learnopengl.com/Getting-started/Coordinate-Systems https://discussions.unity.com/t/comparing-orthographic-and-perspective-cameras/169520 10 Capítulo 2. Introducción al Renderizado Esta transformación implica posicionar la cámara en el origen del sistema de coordenadas, simplificando así los cálculos necesarios para el tipo de proyección (ortográfica o perspectiva) y el culling (eliminación de objetos fuera del campo de visión de la cámara). Este proceso permite que todos los objetos de la escena se representen en relación con la posición y orientación de la cámara, facilitando la determinación de qué objetos están dentro del campo de visión y cuáles están fuera, simplificando así las operaciones para el tipo de proyección. 2.1.3. Iluminación Para añadir cierto realismo a las escenas es necesario introducir luz en ellas. Los diferentes tipos de luces, definidos por su posición, forma y dirección, son elementos fundamentales en la simulación del comportamiento de la luz en entornos virtuales. Se sabe que un objeto se ilumina más cuando está orientado hacia una fuente de luz. Esta orientación determina la cantidad de luz reflejada y, por lo tanto, el brillo del objeto. La orientación en cualquier punto de la superficie del objeto se representa mediante un vector ortogonal llamado normal, que es perpendicular a la superficie en ese punto específico. En esta sección, exploraremos los principales tipos de luces utilizados en infor- mática gráfica, centrándonos en sus características distintivas. 2.1.3.1. Luces puntuales Las luces puntuales emiten luz desde un punto específico en el espacio en 3D, que es su posición. Emiten luz de forma equitativa en todas las direcciones; por ello también se dice que son omnidireccionales (figura 2.8). Por lo tanto, una luz puntual puede describirse completamente en base a su posición e intensidad. Un ejemplo real de luz puntual sería una bombilla (Gambetta, 2021). 2.1.3.2. Luces direccionales Al igual que las luces puntuales, las luces direccionales tienen una determinada intensidad, pero al contrario que ellas, no tienen una posición; en su lugar tienen una dirección fija. De esta forma podemos interpretarlas como luces puntuales situadas a una distancia infinita en la dirección especificada (figura 2.9). Un ejemplo de luz direccional en el mundo real sería el Sol. Aunque emite luz en todas las direcciones y se podría interpretar como una luz puntual, en el caso de la 2.1. Elementos de una escena 11 Figura 2.8: Representación esquemática de la propagación de la luz puntual. Fuente: Borromeo (2020) Tierra, al situarse a tanta distancia todos los rayos que llegan lo hacen casi con la misma dirección (Gambetta, 2021). Figura 2.9: Representación esquemática de la propagación de la luz direccional. Fuente: Borromeo (2020) 2.1.3.3. Luces focalizadas Este tipo de luz representa un cono de luz, como el emitido por una linterna (figura 2.10). Se comporta de manera similar a las luces puntuales en la medida en que su posición y dirección son significativas y la intensidad de la luz decae a cierta distancia (Borromeo, 2020). Cada uno de estos tipos de luces no difieren únicamente en su definición, sino que también hay diferencias a la hora de realizar cálculos: Puntual: Al depender del parámetro de la distancia, es necesario calcularla para determinar su efecto en cada punto. 12 Capítulo 2. Introducción al Renderizado Figura 2.10: Representación esquemática de la propagación de la luz focalizada. Fuente: Borromeo (2020) Direccional: Es la más sencilla y calcular su efecto es rápido porque solo tene- mos en cuenta la dirección de la luz. Foco: Es la más compleja. Al igual que en la puntual, tenemos en cuenta la distancia, pero además es importante considerar el ángulo de la luz para saber si un punto está iluminado, y en qué cantidad. 2.1.4. Viewport El viewport es el área de la pantalla donde se muestra la imagen 2D que proyecta la escena 3D. Para que esta imagen se proyecte adecuadamente en la pantalla nece- sitamos transformar las coordenadas del espacio de la cámara (ver sección 2.1.2) a espacio de recorte, también conocido como clip space. En el clip space, las coordena- das están normalizadas en un rango específico, generalmente entre -1 y 1 en las tres dimensiones (x, y, z). Esto significa que todas las coordenadas de los objetos que caen fuera de este rango no se renderizarán en la pantalla, ya que se consideran fuera del campo de visión. Este es el proceso de “clipping” o recorte. Las coordenadas de recorte (clip coordinates) pueden añadir proyección perspectiva si esta está aplicada en la cámara, a esto se le llama perspective divide (Ilett, 2022). Finalmente este espacio de recorte se convierte a espacio de pantalla o screen space. Las coordenadas de pantalla están directamente relacionadas con los píxeles en la pantalla. La imagen 2D resultante del proceso de renderizado se almacena en el frame buffer. El frame buffer es un espacio de memoria que contiene datos que representan todos los píxeles de un fotograma. Cada píxel en el frame buffer almacena información sobre su color. Una vez que se completa el renderizado de la escena, el contenido del frame buffer se transfiere a la pantalla del dispositivo. 2.2. Formas de renderizado Una vez conocemos el concepto de escena y sus elementos, entra el proceso de generación de una imagen a partir de ella. Existen dos aproximaciones habituales 2.2. Formas de renderizado 13 para hacerlo, la basada en luz y la basada en objetos. A continuación se describen ambas. 2.2.1. Basado en luz Dentro de la generación de imágenes basada en luz, hablamos principalmente de ray tracing y path tracing. El ray tracing se basa en seguir el camino que realiza un rayo de luz al recorrer una escena, mientras interactúa y rebota con los objetos que se encuentran en el entorno (Matt Pharr y Humphreys, 2023). Aunque hay muchas maneras de programar un sistema de ray tracing, todos estos sistemas deben incluir al menos los siguientes objetos y fenómenos: Cámaras: La cámara (sección 2.1.2) define cómo y desde dónde se está vi- sualizando la escena. Muchos sistemas de renderizado generan rayos desde la cámara, que son trazados hasta la escena para determinar qué objetos son visibles en cada píxel. Intersecciones entre rayos y objetos: Es necesario saber de forma precisa dónde un determinado rayo interseca con la geometría de un objeto. Además, es importante definir ciertas propiedades de ese objeto como sus normales o el material de su superficie. Orígenes de las luces: Sin iluminación, no tendría mucho sentido renderizar una escena. Por ello, los sistemas de ray tracing deben simular o modelar la distribución de la luz por la escena incluyendo no sólo las luces en sí, sino también la manera en la que distribuyen su energía en el espacio que conforma la escena. Visibilidad: Para definir si una determinada luz deposita energía en un punto concreto, debemos saber si hay un camino de luz ininterrumpido desde el punto hasta el origen de la luz. Dispersión de la luz en las superficies: Cada objeto debe proporcionar una descripción detallada de su aspecto, incluyendo información sobre cómo interactúa la luz con su superficie, o cómo debe ser su luz dispersa. Transporte de la luz indirecta: Dado que la luz puede llegar a la superficie de un objeto tras haber rebotado en otros objetos o haber pasado por otras superficies, normalmente es necesario trazar rayos adicionales para simular este efecto. Propagación de los rayos: Necesitamos saber qué le sucede a un rayo de luz a medida que se desplaza por la escena, dado que la propagación de la luz depende del entorno en el que se encuentra. 14 Capítulo 2. Introducción al Renderizado A grandes rasgos, el proceso que seguiría un algoritmo de ray tracing sería el que se describe a continuación. Para cada píxel, se lanza un rayo primario en la escena, cuya dirección se de- termina trazando una línea desde la dirección de la cámara a través del centro del píxel. El recorrido de este rayo primario se sigue para determinar si interseca con algún objeto de la escena. En lugar de tratar cada objeto de manera individual, los consideramos como una colección de triángulos debido a su simplicidad geométrica y por el hecho de que son coplanares. Lo que significa que este rayo se interseca con uno de los triángulos formados por la malla del objeto. En escenarios donde ocurren múltiples interseccio- nes, el algoritmo selecciona la intersección más cercana a la cámara para su posterior procesamiento. Luego, se proyecta un rayo secundario, conocido como rayo de sombra, desde este punto de intersección hacía la luz más cercana. Si este segundo rayo interseca con otro objeto en su camino hacia la luz, significa que el objeto está en sombra; de lo contrario, estará iluminado por la luz. Véase la imagen 2.11. Figura 2.11: RayTracing. Fuente: scratchapixel Si el rayo primario interseca con un objeto transparente y no difuso, como un vidrio, se requieren cálculos adicionales en el proceso de ray tracing. En este caso particular, determinar el color implica calcular tanto los colores reflejados como los refractados y combinarlos adecuadamente. Para lograr esto, se generan más rayos, teniendo en cuenta los ángulos de refracción y reflexión. Estos rayos adicionales se lanzan desde el punto de intersección del rayo primario con el objeto y se calculan sus trayectorias basándose en la normal de la superficie, la dirección de rayo incidente y en los ángulos de reflexión y refracción del material. Luego, se determinan los colores de estos rayos secundarios al interactuar con otros objetos en la escena, teniendo en cuenta la iluminación y las propiedades de los materiales. Finalmente, estos colores reflejados y refractados se combinan de acuerdo https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing/implementing-the-raytracing-algorithm.html 2.2. Formas de renderizado 15 con la ecuación para obtener el color final del punto en la superficie del objeto de vidrio. Este proceso puede observarse en la figura 2.12. Figura 2.12: Reflexión y refracción en ray tracing. Fuente: scratchapixel El algoritmo de ray tracing se limita a recrear una serie de efectos como reflexión y refracción intensas o sombras fuertes. Sin embargo, el mismo principio del ray tracing se puede utilizar para resolver todas las cuestiones relativas al renderizado. James Kajiya (Akenine-Moller et al., 2018) descubrió que la mecánica de disparar rayos y evaluar la cantidad de luz que transportaban se podía utilizar para resolver una ecuación integral que manejara las interreflexiones de los objetos de la escena. Esta ecuación era recursiva, lo que implicaba que para cada rayo era necesario re- evaluar la integral en una posición diferente. Afortunadamente, ya existía el método de Monte Carlo para manejar este tipo de problema. La propiedad clave de este método es que solo se necesitan evaluaciones puntuales de la integral. De manera que, con el tiempo suficiente, se puede calcular la integral con precisión arbitraria. En el contexto del renderizado, esto es exactamente lo que proporciona el ray tracing. Cuando disparamos un rayo, tomamos un punto de muestra con el que calcular la integral. A medida que el rayo rebota por la escena, se construye un camino. La luz transportada por cada camino proporciona una evaluación de la integral. Este concepto es el conocido como path tracing. Aunque ha habido intentos de utilizarlo en entornos en tiempo real, los resultados son demasiado prematuros para ser utilizados en este tipo de producción. Por ese motivo, actualmente la práctica más común es utilizarlo para precalcular información relacionada con la iluminación (Akenine-Moller et al., 2018). https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-overview/light-transport-ray-tracing-whitted.html 16 Capítulo 2. Introducción al Renderizado 2.2.2. Basado en objetos La técnica de renderizado basada en objetos es también conocida como raste- rización. Es sin duda la más utilizada para generar imágenes de escenas en 3D de manera eficiente. A diferencia del ray tracing, que traza rayos desde los píxeles hacia la geometría, la rasterización adopta otro enfoque para resolver el problema de la visibilidad. Este enfoque consiste en la proyección de los triángulos de la malla de los objetos sobre la pantalla, transformando una representación 3D en una repre- sentación 2D usando proyección perspectiva u ortogonal. Esto implica proyectar los vértices del triángulo sobre la pantalla y luego rellenar los píxeles de la imagen que quedan cubiertos por el triángulo resultante, como se observa en la figura 2.13. Figura 2.13: Rasterization. Fuente: scratchapixel A simple vista parece un algoritmo sencillo, pero la rasterización enfrenta un desafío crucial: la visibilidad. Determinar qué píxel pintar cuando hay dos objetos superpuestos, uno detrás de otro, implica determinar qué partes de los objetos en 3D son visibles para la cámara. Además, ¿cómo identificamos la presencia de un objeto translúcido que se superpone con otro? Esto nos lleva al problema del orden y la eliminación de superficies. 2.2.2.1. El problema del orden y la eliminación de superficies Este problema es fundamental en la rasterización, ya que implica determinar qué partes de los objetos en 3D son visibles para la cámara. https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/overview-rasterization-algorithm.html 2.2. Formas de renderizado 17 En esta subsección, exploraremos este desafío y las estrategias utilizadas para abordarlo en la renderización basada en objetos. Back face culling: Un modelo 3D esta compuesto por triangulos, y cada uno de estos tiene dos caras: una interna (cara orientada hacia el interior del objeto) y otra externa (cara orientada hacia el exterior del objeto). El back face culling recorre cada triangulo del objeto. Si la cara interior del triangulo está orientada hacia la cámara, es decir, si la cámara “mira” la parte interna del polígono, ese polígono no se dibuja. Solo se dibujarán los polígonos cuya cara exterior esté orientada hacia la cámara. Este método no resuelve el problema de la oclusión entre objetos, es decir, cuando un objeto está delante de otro. El back face culling solo determina si una cara específica de un objeto está orientada hacia la cámara, pero no considera si hay otro objeto en frente que podría bloquear la vista, por lo que ambos objetos se pintarán de manera independiente. Algoritmo del pintor: La técnica más utilizada antiguamente para el ren- derizado de gráficos por ordenador era el llamado “algoritmo del pintor”. Esta técnica se basaba en el renderizado por capas, comenzando por el fondo como haría un pintor con una obra de arte. El algoritmo primero ordenaba los po- lígonos que conformaban la escena según su distancia a la cámara, de manera que los polígonos que se encontraban a mayor distancia se pintaban primero y así sucesivamente hasta llegar a los polígonos más cercanos. Sin embargo este proceso no podía cubrir casos en los que las profundidades de los objetos se intercalaran de la manera que se puede observar en la figura 2.14. Figura 2.14: Problema de la profundidad en el algoritmo del pintor. Fuente: Wikipedia Z-buffer Es un espacio de memoria del tamaño del número de píxeles en pantalla. El Z-buffer almacena la profundidad de cada uno de ellos; es decir, su distancia hasta la cámara, delimitada por el plano cercano y el plano lejano de la cámara, como se puede observar en la figura 2.15. https://es.wikipedia.org/wiki/Algoritmo_del_pintor 18 Capítulo 2. Introducción al Renderizado Figura 2.15: Representación 3D del Z-Buffer. Fuente: Hecktor Docs A la hora de manejar intersecciones y decidir qué píxeles deben pintarse en una posición determinada, si varios objetos de la escena se superponen en el mismo píxel, se comparan las profundidades de estos objetos. Se elige el objeto que esté más cerca de la cámara y se sobrescribe la profundidad en el Z-Buffer, reemplazando el valor anterior. De esta manera, se determina qué objeto está más cercano a la cámara para pintar el píxel correspondiente. Más allá de conocer la profundidad de los píxeles, es importante conocer su opacidad, dado que en determinados casos queremos sobrescribir parcialmente el contenido de un píxel. Esto ocurre por ejemplo cuando tenemos un fondo pero queremos pintar una imagen por delante. En caso de que esta imagen sea completamente opaca, sustituiremos por completo el color de los píxeles del fondo por el color de los píxeles de la imagen que está por delante. Por otra parte si la imagen que queremos pintar por delante es completamente transparente, pintaremos el color de los píxeles que hay en el fondo. Sin embargo, cuando nos encontramos en un punto intermedio en el que tenemos un fondo y una imagen que no es completamente transparente pintada por encima, tenemos que realizar cálculos adicionales para componer una imagen que tenga en cuenta cómo interactúan estos dos elementos clave, es decir: el fondo y la imagen superpuesta (Shirley et al., 2009). El proceso de alpha blending consiste en combinar elementos con transparencia, ya sea con un fondo determinado u otros elementos de la escena. Los píxeles, además de su color, también pueden tener información sobre su porcentaje de transparencia, conocido como el “canal alfa”, de ahí el término alpha blending. Este canal alfa del píxel se tiene en cuenta en el proceso de combinación. En la figura 2.16 se muestra una de las fórmulas más comúnmente utilizadas para realizar los cálculos necesarios para llevar a cabo esta operación. Al utilizar alpha blending los componentes no se pueden renderizar en un orden https://docs.hektorprofe.net/graficos-3d/24-profundidad-con-z-buffer/ 2.2. Formas de renderizado 19 arbitrario, sino que deben renderizarse primero aquellos que sean opacos y posteriormente los demás en un orden desde atrás hacia delante (Han, 2018). Figura 2.16: Representación de la aplicación de una fórmula típica de mezclado. Fuente: Han (2018) 2.2.2.2. El problema de los efectos globales Si queremos crear efectos globales como sombras o reflejos completos a modo de espejo, tenemos que realizar trabajo adicional debido a que el método de dibujado se basa en objetos. Esto limita la cantidad de efectos que podemos llegar a simular al utilizar este método de renderizado. Muchas de las superficies en el mundo real reciben gran parte de su luz incidente de otras superficies reflectoras. Este fenómeno se conoce generalmente como luz indirecta o iluminación mutua. Por ejemplo, los techos de la mayoría de habitaciones reciben poca o prácticamente ninguna iluminación directamente de objetos emisores de luz, pero podemos observarlos por la luz indirecta que reciben. Dado que cualquier objeto podría iluminar a cualquier otro objeto, la iluminación indirecta produce el problema de la iluminación global (Shirley et al., 2009). Este problema se puede evitar en el renderizado basado en luz ya que, por su naturaleza, la iluminación global se calcula en el propio algoritmo de ray tracing o path tracing. Sin embargo en el renderizado basado en objetos es necesario recurrir a otras técnicas. Puesto que los objetos se están renderizando de forma individual, se asume que la visualización de un objeto no afecta a los demás. Sin embargo, esto no ocurre así; por ejemplo, un objeto opaco puede ocultar a otro tras él, haciendo que este segundo no se vea. Más allá del culling, los objetos interactuán entre sí en lo referente a la iluminación, como es el caso de la iluminación indirecta de un objeto sobre otro, la proyección de sombras, o los reflejos. Por tanto, los objetos están “interactuando” entre ellos y hay que afrontarlo de alguna manera. 20 Capítulo 2. Introducción al Renderizado Para conseguir estos “efectos globales” causados por las interacciones entre ob- jetos independientes, el renderizado basado en objetos tiene que emplear técnicas adicionales. Por ejemplo, las sombras generalmente son “pintadas” sobre los objetos ya renderizados. Esto puede lograrse en tiempo real, utilizando técnicas como el shadow mapping, las stencil shadows, o bien, precalculándolas cuando las fuentes de luz sean estáticas, haciendo uso de lightmaps. En cuanto a los reflejos, normalmente se recurre a renderizar la escena múltiples veces en imágenes adicionales que luego son proyectadas sobre las superficies de los objetos reflectantes. 2.3. Materiales e interacción con la luz Los materiales de los objetos de una escena 3D son aquellos elementos que pro- porcionan información sobre el aspecto visual de los mismos. Especifican la manera en la que cada objeto interactúa con la luz, lo que determina el color final en cada píxel del objeto. Como vimos anteriormente en la sección 2.2.1, usar métodos de renderizado basado en luz proporciona efectos muy realistas pero resulta bastante costoso. En el renderizado basado en objetos utilizamos modelos que nos permiten aproximar la interacción entre los materiales de un objeto con la luz de forma menos realista pero más eficiente. En estas secciones explicaremos algunos de los conceptos clave que hacen que los materiales se vean de una forma u otra dependiendo de criterios como el modelo de sombreado o el modelo de iluminación utilizados. Sin embargo antes de entrar en estos conceptos es importante introducir lo que son las normales y cuál es su uso en este contexto. 2.3.1. Normales El vector normal, comúnmente conocido como “la normal”, de un plano es un vector perpendicular que sirve para identificar su dirección (figura 2.17a). Como consecuencia, la normal de un polígono es la normal del plano en el que está inscrito y la normal en un vértice es una aproximación de la normal en ese punto de la superficie (figura 2.17b). Estos vectores normales son representados típicamente como vectores unitarios, es decir, vectores con una longitud de 1, lo que permite simplificar los cálculos y garantizar que solo se considere la dirección perpendicular, sin importar la magnitud del vector. Cuando tratamos con superficies solemos considerar la existencia de dos vectores normales en un plano, uno que representa la dirección del plano hacia el “exterior” de la superficie y otro que representa la dirección hacia el “interior” de esta, como se observa en la figura 2.17a. Sin embargo, de ahora en adelante haremos referencia 2.3. Materiales e interacción con la luz 21 (a) Representación gráfica de un plano con sus normales. Fuente: Wikipedia (b) Normales de los vértices de un dodecaedro. Fuente: Wikipedia únicamente a aquellas normales que apuntan hacia el exterior de la superficie. La capacidad de las normales para proporcionar información representativa de superficies o de puntos clave del modelo como son los vértices, hacen de estas un recurso idóneo para realizar cálculos referentes a la iluminación, debido a que la orientación de una superficie es un factor fundamental a la hora de conocer cómo se refleja la luz en ella. Por ello dependiendo de la cantidad de información que tengamos sobre un mo- delo y sus normales, podremos realizar aproximaciones más o menos realistas de la interacción entre dicho modelo y la iluminación. 2.3.2. Sombreado El sombreado y la iluminación son conceptos diferentes pero están fuertemente relacionados. En la sección 2.1.3, evaluamos los diferentes tipos de luces y los pará- metros necesarios para calcular sus efectos sobre los objetos. El sombreado consiste en técnicas que extienden el efecto de la luz desde un conjunto discreto de puntos, a objetos enteros (Gambetta, 2021). Podemos calcular cómo se extiende el efecto de la luz sobre un objeto siguiendo diferentes modelos de sombreado, representados en la figura 2.18. 2.3.2.1. Sombreado plano Es el modo más sencillo de todos ya que cada polígono del modelo queda repre- sentado por una sola normal y un solo color. Como solo realizamos los cálculos de iluminación para un punto del polígono (su normal), el resultado de ese cálculo será https://es.wikipedia.org/wiki/Vector_normal https://en.wikipedia.org/wiki/Vertex_normal 22 Capítulo 2. Introducción al Renderizado (a) Sombreado plano. (b) Sombreado Gouraud. (c) Sombreado Phong. Figura 2.18: Comparativa de los modelos de sombreado aplicados sobre un mismo modelo. Fuente: (Gambetta, 2021) el que se utilice para representar al polígono al completo. Por lo tanto, al realizar cálculos a mayor escala que otros, este método de som- breado no permitirá representar muchos de los matices que aporta la iluminación a los modelos. Aunque se trata de un método que puede ser útil para objetos más sencillos, muestra grandes discontinuidades fácilmente visibles entre los límites de los polígonos más complejos. Se puede visualizar un ejemplo claro de este efecto en la figura 2.18a. 2.3.2.2. Sombreado Gouraud En modo Gouraud, cada vértice tiene una normal y un color. Supone un mayor número de cálculos respecto al modelo anterior ya que se calcula el efecto de la luz en cada uno de los vértices y además se interpolan los colores entre cada vértice. Esta interpolación de los colores reduce abundantemente las discontinuidades entre polígonos del modelo anterior; sin embargo una de sus mayores limitaciones es la representación de reflejos, como se observa en la figura 2.18b. 2.3.2.3. Sombreado Phong En el modo Phong, como en el modelo anterior, cada vértice tiene una normal y un color; pero además de interpolarse los colores, resolviendo el problema de las discontinuidades, se interpolan las normales y se calcula el efecto de la ilumina- ción para cada píxel. Estos cálculos permiten resolver las limitaciones del modelo Gouraud con respecto a la representación de los reflejos (figura 2.18c). 2.3. Materiales e interacción con la luz 23 2.3.3. Modelo de iluminación Teniendo información sobre el material de un objeto, sus normales y la luz de la escena en la que se encuentra, podemos calcular el color de un objeto. El modelo de iluminación es lo que determina cómo se calcula el resultado visual a partir de la información recibida. En el pipeline fijo clásico, que se explicará en la sección 2.4, el modelo de ilumi- nación tenía en cuenta los siguientes componentes: (a) Componente ambiente (b) Componente difusa (c) Componente especular Figura 2.19: Componentes esenciales para representar el color de un objeto. Fuente: Wikipedia Componente de ambiente: Como se vio en la sección 2.2.2.2, los objetos de una escena siempre van a recibir una cierta cantidad de luz indirecta. El componente ambiente representa este efecto, como se ve en la figura 2.19a. Dado que es un elemento uniforme, es el componente menos costoso de calcular, puesto que solo depende de la intensidad que le sea asignada. Este componente se suma directamente al color final. Componente difusa: La componente difusa se computa teniendo en cuen- ta los puntos de luz que afectan al objeto. Esta componente supone realizar cálculos más complejos ya que requiere calcular el ángulo con el que la luz incide en las normales del objeto. La componente tiene más peso en los puntos en los que la luz incide de forma más perpendicular (figura 2.21b). Componente especular: Se origina debido a que la luz que alcanza la super- ficie de un objeto se refleja prácticamente en el mismo ángulo. Esto implica que siempre hay una parte de la superficie que refleja muchos de los rayos de luz de vuelta al observador de forma que se puede apreciar un brillo más intenso en la superficie del objeto (figura 2.19c). Esto ocurre en objetos con propiedades reflectantes y varía dependiendo del ángulo de visión del observador, por lo tanto es la componente más costosa de calcular (Ilett, 2022). https://en.wikipedia.org/wiki/Phong_reflection_model 24 Capítulo 2. Introducción al Renderizado El resultado de aplicar estas componentes siguiendo el modelo de sombreado Phong explicado en la sección 2.3.2.3, se muestra en la figura 2.20. Figura 2.20: Suma de las componentes y aplicación del modelo Phong. Fuente: Wikipedia A día de hoy, existen modelos de iluminación mucho más elaborados, que toman en cuenta otros parámetros de entrada del material para realizar los cálculos de ilu- minación. A continuación, se describen algunos de los parámetros más comunes en los modelos de iluminación actuales. Estos parámetros permiten modificar los mate- riales de innumerables maneras; mediante su ajuste, los materiales pueden adoptar apariencias muy diversas según lo que se desee lograr. Color Base (Base Color): Color del material. Metálico (Metallic): Permite controlar el color y la fuerza de los reflejos metálicos en el material (figura 2.21a). Especular (Specular): Permite controlar el color y la fuerza de los reflejos especulares en el material (figura 2.21b). Emisión (Emission): Hace que parezca que la superficie emite luz (figura 2.21c). (a) Material con mucha metalicidad. (b) Material muy especular. (c) Material con mucha emisión. https://en.wikipedia.org/wiki/Phong_reflection_model 2.3. Materiales e interacción con la luz 25 2.3.4. Texturas Los parámetros de los materiales tal y como se describen en la sección anterior, pueden utilizare para especificar color. Esta información se puede proporcionar para el objeto completo, para cada vértice o para cada píxel. En este último caso se hace uso de lo que se conoce como texturas, imágenes que se aplican al objeto, a modo de “envoltorio” para definir su apariencia visual. Las texturas se dividen en dos secciones: Texturas por imagen: Un mapa de bits es una matriz de píxeles, donde cada píxel tiene un color específico representado por canales separados que representan los componentes primarios del color. Por lo tanto, una textura es una imagen de mapa de bits que se aplica sobre la superficie del objeto 3D (figura 2.22). Figura 2.22: Mapeo de una textura en una malla. Fuente: HaroldSerrano Texturas procedurales: Las texturas procedurales se generan mediante des- cripciones matemáticas y algoritmos, en lugar de datos almacenados directa- mente en un mapa de bits. Esto ofrece varias ventajas, como un bajo costo de almacenamiento, resolución ilimitada de la textura y un fácil mapeo de texturas a la malla. Para posicionar las texturas, tanto por imagen como procedurales, normalmente se usan las UVs. Las UVs funcionan como un mapeo bidimensional de la textura sobre la superficie tridimensional del objeto. Cada vértice de la malla del modelo u objeto tiene asignada una coordenada UV específica, que determina qué parte de la textura se aplicará en dicho vértice. Para los puntos de la superficie que no coinciden exactamente con un vértice, se interpolan las coordenadas UV entre los vértices más cercanos (figura 2.23). Al hablar de las texturas surge el concepto del baking . El baking es un proce- so que consiste en precalcular información sobre un modelo 3D y plasmarla sobre una textura. Estas texturas pueden contener información precalculada sobre la ilu- minación global y las sombras o permiten representar texturas procedurales como texturas por imagen. https://www.haroldserrano.com/blog/applying-textures-to-3d-objects-in-metal 26 Capítulo 2. Introducción al Renderizado Figura 2.23: UVs de una malla 2.3.5. Renderizado basado en físicas y función de distribución de dispersión bidireccional La función de distribución de reflectancia bidireccional (Bi-Directional Reflec- tance Distribution Function, BRDF) es una función que define cómo la luz se refleja en una superficie opaca. Se emplea tanto en la óptica de la luz del mundo real, como en algoritmos de gráficos computacionales. Por otra parte, la función de distribución de transmitancia bidireccional (Bi- Directional Transmittance Distribution Function, BTDF) es similar a la BRDF, pero para la transmisión de la luz hacia el otro lado de la superficie, tal y como se puede observar en la figura 2.24. De la combinación de estas funciones surge la función de distribución de disper- sión bidireccional (Bi-Directional Scattering Distribution Function, BSDF) al tenerse en cuenta no solo cómo se refleja la luz en un objeto, sino también cómo se transmite a través de este. Describe cómo se dispersa la luz cuando golpea una superficie en un punto dado y realiza estos cálculos teniendo en cuenta la dirección de la luz entrante así como la dirección de la luz que sale describiendo cuánta se absorbe, cuánta se dispersa en diferentes direcciones y cuánta se refleja (Hoffman, 2013). Este efecto puede observarse en la figura 2.24. Los fundamentos del renderizado basado en físicas (Physically Based Rendering, PBR) son las leyes físicas del mundo real, así como sus expresiones matemáticas. Por ello, para conseguir un renderizado realista es necesario hacer uso de las funciones anteriores que expliquen cómo interactúa la luz con los objetos de nuestra escena. El renderizado basado en físicas no está exclusivamente reservado para técnicas de renderizado basadas en luz (sección 2.2.1), y sus principios pueden ser aplicados en el proceso de rasterización (sección 2.2.2), con el objetivo de aportar mayor realismo a las escenas representadas. El PBR en el renderizado basado en objetos se implementa principalmente me- diante el uso de funciones matemáticas que describen cómo la luz se refleja, refracta 2.4. Pipeline gráfico 27 Figura 2.24: BSDF = BRDF + BTDF. Fuente: Wikipedia y dispersa en la superficie de un objeto. Esto supone la creación de un modelo de iluminación en el que se simule la realidad, en el cual los parámetros de los mate- riales tienen significado físico, como ocurre en las propiedades explicadas al final de la sección 2.3.3. El cálculo del color de cada píxel utiliza esa información y la física de la luz para aproximarse a la realidad. Aunque esta aproximación no alcanza la exactitud de técnicas como el ray tracing, que simula el comportamiento de la luz de manera extremadamente precisa, sí puede producir resultados visuales de alta calidad. 2.4. Pipeline gráfico A partir de ahora, nos centraremos en el renderizado basado en objetos para la generación de imágenes debido a su menor costo computacional y su capacidad para ofrecer tiempos de renderizado más rápidos que el renderizado basado en luz. En las siguientes secciones se explican conceptos importantes para comprender el funcionamiento de este modo de renderizado. El pipeline gráfico o render pipeline es la herramienta que hay tras el proceso de renderizado en tiempo real. Su principal función es generar o renderizar una imagen bidimensional, dada una escena, con los elementos que esta contiene. En la figura 2.25 se puede observar el trabajo que realiza el render pipeline para obtener una representación bidimensional de una escena con diferentes elementos (Akenine- Moller et al., 2018). Previamente en la sección 2.1.1 se han descrito los distintos espacios de coor- denadas, y el objetivo en esta sección es sintetizar esa información y comprender https://en.wikipedia.org/wiki/Bidirectional_scattering_distribution_function 28 Capítulo 2. Introducción al Renderizado (a) Vista de una escena con una cámara virtual. (b) Resultado generado por el render pipeline. Figura 2.25: Ejemplo del proceso de renderizado que realiza el render pipeline (Akenine-Moller et al., 2018). paso a paso cómo el render pipeline las utiliza para generar una imagen, partiendo únicamente de los datos tridimensionales que contiene la escena. A continuación se describen los pasos que sigue el render pipeline para transformar las coordenadas iniciales al espacio de vista bidimensional que debe producir. Este proceso puede apreciarse visualmente en la figura 2.26. 1. Espacio del Modelo (Model Space) No es un paso del proceso, sino las coordenadas iniciales de los vértices en el sistema de coordenadas del modelo o locales. Es lo que recibe el render pipeline y sobre lo que se comienza a operar. Nombramos como: Vms a las coordenadas del vértice en el espacio local o de modelo. 2. Transformación al Espacio del Mundo (World Space) Se aplica a la Matriz del Modelo (Model Matrix), que posiciona y orienta el objeto en coordenadas de mundo. Si denotamos como: Vws las coordenadas del vértice en el espacio de mundo. M a la matriz de modelo. entonces, la fórmula general es: Vws = MVms 3. Transformación al Espacio de la Vista (View Space) Se aplica a la Matriz de Vista (View Matrix), que transforma las coordenadas del mundo al sistema de coordenadas de la cámara o vista. Si denotamos por: Vvs las coordenadas del vértice en el espacio de la vista. 2.4. Pipeline gráfico 29 C a la matriz de vista. entonces, la fórmula general es: Vvs = CVws 4. Transformación al Espacio de Proyección (Clip Space) Se aplica la Matriz de Proyección (Projection Matrix), que proyecta las coor- denadas de la cámara en un volumen de visualización. Esta matriz puede ser de dos tipos: proyección ortográfica y proyección en perspectiva. Si denotamos como: Vcs las coordenadas del vértice en el espacio de proyección. P a la matriz de proyección. entonces, la fórmula general es: Vcs = PVvs En esta etapa, el vértice se transforma en lo que se denomina coordenadas homogéneas 4D (x, y, z, w), donde w es un valor crucial para la siguiente trans- formación. El valor de w generalmente representa la distancia del vértice a la cámara y actúa como un factor de escala en la proyección de perspectiva. 5. Transformación al Espacio Normalizado de Dispositivo (Normalized Device Coordinates, NDC ) Para normalizar las coordenadas NDC se hace uso de la división de perspec- tiva, que convierte las coordenadas homogéneas 4D de Vep en coordenadas 3D normalizadas. Esto se hace dividiendo cada componente x, y y z por el componente w. vértice en NDC = ( x w , y w , z w ) Para proyecciones con perspectivas este paso es crucial para mantener la co- herencia de la perspectiva, simulando cómo los objetos se ven más pequeños cuando están más lejos de la cámara. El resultado de esta transformación son las Coordenadas Normalizadas de Dispositivo (NDC), donde los valores de x, y y z estarán en el rango de −1 a 1 (aunque este rango puede depender de la API). En una proyección ortográfica, el proceso de normalización es más directo. El frustum ortográfico mapea todas las coordenadas dentro del frustum a las coordenadas normalizadas sin modificar el componente w del vector transfor- mado. Si el componente w permanece igual a 1.0, la división de perspectiva no alterará las coordenadas: vértice en NDC = (x 1 , y 1 , z 1 ) Esto significa que no hay una división efectiva por w, y los vértices simplemente se normalizan dentro del rango de −1 a 1. 30 Capítulo 2. Introducción al Renderizado x entre −1 y 1 corresponde al ancho de la pantalla. y entre −1 y 1 corresponde a la altura de la pantalla. z entre −1 y 1 corresponde a la profundidad, donde −1 es el plano cercano y 1 es el plano lejano. Si alguna de las coordenadas resultantes se encuentra fuera de este rango, será recortada, es decir, no se mostrará en la pantalla final. 6. Transformación al Espacio de Pantalla (Screen Space) Se aplica la Transformación de Viewport (Viewport Transformation), que ma- pea las coordenadas normalizadas a las coordenadas de la pantalla. Si deno- tamos como: Vss las coordenadas del vértice en el espacio de pantalla screen space. F a la matriz de transformación de viewport. entonces, la fórmula general es: Vss = FVNDC Figura 2.26: Transformaciones hasta llegar a una representación 2D.Fuente:LearnOpenGL Antiguamente, las funcionalidades de las que disponían los programadores de gráficos eran muy limitadas. Contaban con lo que conocemos como la fixed-function pipeline, o pipeline de renderizado fijo. Con ella se podía configurar una pequeña variedad de funciones especializadas para modificar aspectos como la iluminación o las texturas. A pesar de que esto mejoró los métodos de renderizado software que había anteriormente, seguía siendo muy limitado. Del deseo de obtener un control más preciso sobre el proceso de renderizado, surgió el pipeline de renderizado pro- gramable. Esto nos permite controlar completamente el comportamiento de algunas https://learnopengl.com/Getting-started/Coordinate-Systems 2.4. Pipeline gráfico 31 partes del proceso de renderizado, utilizando programas llamados shaders. Ya no hay una limitación para usar únicamente una serie de funciones, sino que se puede escribir código directamente para dar instrucciones a la tarjeta gráfica, consiguiendo una gran cantidad de posibles combinaciones de efectos (Ilett, 2022). El pipeline de renderizado programable se divide en varias etapas, que se pueden observar en la figura 2.27. Algunas de estas etapas son fijas (fixed-function stages) y otras son programables (programmable functions). En las etapas fijas, se pueden ajustar las operaciones utilizando parámetros, pero su funcionamiento está predefini- do, como ocurría antaño con el fixed-function pipeline. Por el contrario, en las etapas programables, se puede cargar el código personalizado que se mencionaba anterior- mente en la tarjeta gráfica, para aplicar operaciones específicas según el resultado deseado. En la figura 2.27, el color amarillo indica las funciones programables y el verde, las funciones fijas. Ahora, procederemos a explicar cada una de estas etapas en más detalle. 2.4.1. Shader Un shader es un programa informático utilizado por la GPU, especialmente en entornos 3D, para determinar las propiedades de los píxeles en una imagen o su- perficie. Las instrucciones que contiene el shader son ejecutadas para cada píxel de la pantalla, por lo que los cálculos realizados dependen de los valores de posición e iluminación de dicho píxel. Estos cálculos permiten simular efectos realistas como sombras, reflejos, refracciones y otros fenómenos visuales (Bailey y Cunningham, 2012). Figura 2.27: Etapas del pipeline de renderizado. Las etapas encuadradas de color amarillo representan las partes del pipeline que se pueden personalizar usando shaders. Fuente: Intro Vulkan https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Introduction 32 Capítulo 2. Introducción al Renderizado Input Assembler: El input assembler recoge los datos de los vértices en crudo a partir de los buffers que se especifiquen. Será con estos datos de entrada con los que el hardware realice sus operaciones en las diferentes etapas. Es importante que cada etapa pase esta información a la siguiente para que no se pierda. Vertex Shader: El procesado de vértices en el pipeline de renderizado se rea- liza principalmente a través del vertex shader. Esta función es responsable de transformar los vértices de los objetos desde su espacio original en coordenadas locales (model space, véase la sección 2.1.1) hasta las coordenadas de pantalla (clip space, véase la sección 2.1.4). Es necesario determinar las coordenadas de cada píxel procesado dentro del espacio de la ventana. Para ello, se necesitan las primitivas, los vértices en su propio espacio y la definición de una serie de operaciones matemáticas que calcularán dicha posición en la pantalla para cada vértice (Bailey y Cunningham, 2012). Durante este cálculo se aplicarán las transformaciones necesarias a cada vértice de manera individual. Además de las transformaciones geométricas, en el vertex shader también se pueden incluir cálculos de iluminación, donde se determina cómo la luz afecta a cada vértice en función de su posición y de las propiedades del material. Dentro del procesado de los vértices, entra el concepto de Matriz de modelo- vista o ModelView Matrix. Es la matriz utilizada para transformar cada vértice desde el espacio de modelo (model space) al espacio de la cámara (camera space o view space, véase la sección 2.1.2.1). A continuación, se vuelve a transformar el vértice aplicando la transformación de proyección para trasladar el vértice a clip space o espacio de recorte. Estos dos procesos suelen hacerse de manera conjunta, operando el vértice original con una única matriz ModelViewProjection que transforma las coordenadas locales a coordenadas de mundo y luego a clip space o espacio de recorte en una única operación. Finalmente, se lleva a cabo la transformación del viewport, convirtiendo los vértices en coordenadas de pantalla a píxeles. Se mapean los vértices al espacio de la ventana final en la que se mostrará la escena. Tessellation Shader: La teselación es el proceso de subdividir geometría en partes más pequeñas, lo que permite representar objetos de manera más detallada y realista en la pantalla. Entre otras cosas, los shaders de teselación permiten realizar una subdivisión adaptativa de la geometría para aumentar la calidad de las imágenes, gestionar el nivel de detalle de la calidad de imagen, o aplicar mapas de desplazamiento sin definir una geometría detallada. Geometry Shader: El principal propósito de la etapa de shader de geometría es modificar la geometría de la malla que se pasa a través de ella. Se pueden crear nuevos vértices, cambiar vértices o incluso eliminar vértices y crear una malla completamente nueva. Esto se usa, por ejemplo, para la creación de cabello o hierba. 2.4. Pipeline gráfico 33 Rasterización: Durante el proceso de rasterización, los triángulos que compo- nen los objetos en una escena tridimensional se transforman en una cuadrícula 2D de fragmentos, que representan los píxeles en la pantalla. En esencia, la rasterización convierte la información geométrica de los objetos en una ima- gen discreta del tamaño de la ventana de visualización, conocida como frame buffer. Este proceso es crucial para la representación visual en pantalla, ya que permi- te que los objetos tridimensionales se proyecten adecuadamente en la pantalla bidimensional. El rasterizador, que puede ser parte de la GPU o la CPU (im- plicando un bajo rendimiento), es el componente encargado de llevar a cabo esta tarea. Fragment Shader: Una vez se han calculado las posiciones de los píxeles, se debe determinar su apariencia visual. Para ello se determinan los fragmentos, que conoceremos como las unidades básicas de renderizado en la pantalla. A diferencia de un píxel, un fragmento puede no corresponder directamente a una unidad visual en la pantalla, ya que múltiples fragmentos pueden con- tribuir al resultado visual correspondiente a un único píxel. Estos fragmentos se obtendrán gracias a las primitivas y los vértices ya procesados. En el frag- ment shader se calculará el color, textura y otros tantos atributos visuales que determinarán el aspecto final de dicho fragmento. Estos cálculos se realizan en función de parámetros como la iluminación, los materiales y las texturas asociados al fragmento. Esta es la sección donde se determina la información visualmente más interesante del material. Color blending: Los colores del fragment shader no son los que se muestran finalmente en el viewport (véase la sección 2.1.4). Una vez que el fragment shader ha terminado, ocurre esta última etapa de procesado. El color blen- ding incluye una prueba de profundidad, donde los fragmentos opacos pueden ser descartados si están detrás de otro objeto opaco, o puede realizarse una mezcla, donde los colores de objetos semitransparentes se combinan con los colores que ya se han dibujado en la pantalla. La entrada que recibe el color blending se conoce como “RGBAZ fragment”, donde “A” representa la opaci- dad en un rango normalizado [0,1] siendo 0 completamente transparente y 1 completamente opaco; y “Z” representa la profundidad. 2.4.2. Programación de shaders Para escribir shaders existen varios lenguajes de programación. A continuación se exponen tres de los más usados: Cg (C for graphics) En los inicios, la programación del vertex y fragment shader se realizaba exclu- sivamente a un nivel muy bajo, utilizando únicamente lenguaje ensamblador. Sin embargo, con el avance de la tecnología de las tarjetas gráficas, surgió la 34 Capítulo 2. Introducción al Renderizado necesidad de simplificar este proceso. NVIDIA desarrolló Cg en respuesta a es- ta necesidad, creando un lenguaje de alto nivel que permitiera la modificación del pipeline de renderizado, así como la manipulación de los vertex shader y fragment shader. Este lenguaje esta ya obsoleto y no se recomienda su uso en proyectos actuales. HLSL (High Level Shader Language) Es un lenguaje de programación diseñado por Microsoft específicamente para Direct3D. HLSL ofrece una sintaxis similar a la de los lenguajes de programa- ción de alto nivel, como C o C++, lo que facilita su uso para los desarrolladores. Este lenguaje fue creado con el propósito de permitir la configuración completa del pipeline 3D programable. HLSL se desarrolló de forma conjunta con Cg, por lo que son lenguajes muy parecidos. GLSL (OpenGL Shading Language) Los lenguajes de sombreado fueron desarrollados para dar acceso al programa- dor gráfico a una gran cantidad de operaciones del hardware. El lenguaje de GLSL se diseñó para ser independiente de la plataforma y ha sido parte del estándar desde OpenGL 2.0 en adelante. Permite la independen- cia del dispositivo teniendo compiladores integrados en el driver de la tarjeta gráfica, que traducen el código de GLSL a instrucciones específicas para esa tarjeta gráfica. El proceso de vincular shaders a un programa, compilarlo y vincularlo de forma que sea descargado en la tarjeta gráfica es parte de la API de GLSL (Bailey y Cunningham, 2012). Este tipo de lenguajes están orientados a programadores, pero para que los ar- tistas puedan “programar” shaders sin tener que trabajar a tan bajo nivel, existen editores visuales que proporcionan una abstracción más comprensible. Algunos de los programas que utilizan editores visuales para la programación de shaders son Unreal Engine, Unity y Blender. Estos editores visuales utilizan nodos, que se conectan entre sí para definir el comportamiento y las propiedades de los shaders. Por ejemplo, un nodo de textura puede conectarse al nodo de color base, definiendo así el color del shader a partir de una imagen. Este enfoque facilita la creación de comportamientos y efectos complejos sin necesidad de escribir código directamente, haciendo que la programación de shaders sea accesible incluso para aquellos sin conocimientos avanzados en lenguajes como HLSL o GLSL. La definición del funcionamiento del pipeline gráfico tal y como se ha explicado hasta ahora, corresponde con lo que se conoce como el pipeline gráfico forward o forward render pipeline. Es el enfoque tradicional utilizado en gráficos por compu- tadora donde cada objeto de la escena se procesa por completo en cada etapa del pipeline, desde los cálculos de los shaders hasta la rasterización, para luego aplicar el modelo de iluminación y renderizar los píxeles. Sin embargo, este método puede ser ineficiente cuando se trata de escenas complejas con un gran número de luces, ya 2.4. Pipeline gráfico 35 que el modelo de iluminación se debe calcular por cada objeto de la escena, lo que puede resultar en un rendimiento deficiente. Para solucionar este problema, existe el pipeline gráfico deferred. 2.4.3. Pipeline gráfico deferred El renderizado diferido o deferred parte de la idea de calcular la iluminación como el último estado del proceso de renderizado. Es útil a la hora de utilizar grandes cantidades de luces, ya que en un caso así, en comparación con el método de forward rendering realiza menos cálculos por cada luz utilizada. Tras determinar qué objetos se encuentran dentro de la vista de cámara y orde- narlos, el camino de renderizado diferido genera lo que se conoce como Geometry Buffer o G-Buffer. El Geometry Buffer es un espacio de memoria que contiene infor- mación sobre los objetos de la escena como los colores de sus píxeles, las normales de cada píxel y cómo de cercanos a la cámara se encuentran. La figura 2.28 muestra un ejemplo de G-Buffer. (a) Colores Planos del Objeto. (b) Profundidad de cada píxel. (c) Normales de cada píxel. Figura 2.28: Ejemplo de contenido del G-buffer. Fuente: Borromeo (2020) Tras procesar este primer pase de los shaders, se ejecuta un segundo pase diferido aplicando una capa de iluminación sobre el G-Buffer y obteniendo información de este para calcular cada luz específica. Tras procesar todas las luces el resultado será el observado en la figura 2.29. El uso del renderizado diferido permite evitar cálculos innecesarios que se realizan en forward rendering, puesto que no se calcula la iluminación sobre un objeto que no será visto en la escena final al ser ocultado por otro más cercano a la cámara. 2.4.4. Ventajas y desventajas de deferred y forward rendering El deferred rendering presenta una serie de ventajas, especialmente en escenas con un gran número de luces. Su principal ventaja radica en la eficiencia en el manejo de estas luces, ya que el costo de añadir una nueva luz es relativamente bajo. Esto 36 Capítulo 2. Introducción al Renderizado Figura 2.29: La combinación de las tres luces que han sido aplicadas al G-Buffer en el paso mostrado anteriormente. Fuente: Borromeo (2020) se debe a que los cálculos de iluminación se realizan en un único pase utilizando los datos almacenados en el G-Buffer. Sin embargo, el deferred rendering tiene desventajas notables, como el uso ele- vado de memoria y de ancho de banda debido al almacenamiento del G-Buffer. Además, presenta dificultades en el manejo de transparencias, ya que está diseñado principalmente para objetos opacos. El G-Buffer no puede almacenar información de todas las capas de renderizado, lo que limita su capacidad para soportar objetos transparentes. Por otro lado, el forward rendering puede manejar transparencias de manera más directa, renderizando objetos transparentes en el orden correcto para la mez- cla de colores. Además, utiliza menos memoria, lo que puede ser beneficioso para dispositivos con recursos limitados. No obstante, el forward rendering también tiene sus desventajas. El costo de renderizado aumenta con el número de luces, ya que cada luz se aplica por objeto. Conclusiones En este capítulo sobre el renderizado se ha expuesto el proceso fundamental que explica cómo se convierten las escenas tridimensionales a imágenes bidimensionales. Han sido presentadas las diferentes maneras de realizar este proceso de renderizado y las características de cada una. Al comprender los elementos que forman parte de una escena, la interacción entre los materiales y la iluminación, y las etapas del render pipeline, se han sentado las bases para entender cómo funcionan los motores de renderizado en entornos como Unity y Blender. Un motor de renderizado se puede considerar un “complemento” al software 3D que ayuda a renderizar las escenas de manera más o menos sencilla, dependiendo 2.4. Pipeline gráfico 37 del motor, para el usuario. Estos motores utilizan los conceptos explicados en es- te capítulo para permitir a los artistas y desarrolladores crear entornos virtuales que puedan visualizar en sus pantallas. En los próximos capítulos, exploraremos en profundidad cómo se utilizan estas herramientas, poniendo especial énfasis en cómo renderiza cada plataforma. Capı́tulo 3 Blender Blender1 es una suite de creación en 3D gratuita y de código abierto que soporta todo el proceso de creación 3D, dedicado especialmente a la creación de modelos 3D, su iluminación, su visualización, y su animación . Además permite la edición de vídeo e incluso la creación de juegos. Más allá de los artistas especializados en la escultura digital o la animación, los usuarios con conocimientos avanzados en programación emplean la API de Python de Blender para personalizar la aplicación y escribir herramientas especializadas; a menudo, éstas se incluyen en futuras versiones de Blender. Debido a que Blender es un software gratuito con un código fuente fácilmente accesible, y una gran comunidad de usuarios, lo hemos considerado como la herra- mienta ideal para enfocar este proyecto dejando de lado alternativas como Autodesk Maya o Zbrush que suponen un pago o suscripción y son accesibles a un público más reducido. Blender es multiplataforma y está disponible en Linux, Windows y Macintosh. Su interfaz utiliza OpenGL para proporcionar una experiencia consistente. Como proyecto impulsado por la comunidad bajo la Licencia Pública General de GNU (GPL), el público tiene el poder de realizar cambios pequeños y grandes en el código base, lo que conduce a nuevas características, correcciones de errores y una mejor usabilidad. En este capítulo se expone el funcionamiento de Blender, explicando los objetos y materiales que los usuarios de este programa crean y sitúan en las escenas. El objetivo es facilitar la comprensión de la herramienta desarrollada, tanto desde el punto de vista de diseño como desde la implementación. Además, se exponen los diferentes modos de renderizado disponibles en Blender, justificando la selección de uno de ellos para el desarrollo del proyecto. 1https://www.blender.org/ 39 https://www.blender.org/ 40 Capítulo 3. Blender 3.1. Objetos en Blender La geometría de una escena se construye a través de uno o más objetos 2 (descritos de forma general en la sección 2.1.1). Estos objetos pueden ser desde formas básicas en 2D, a modelos 3D o elementos más complejos como luces o cámaras. Cada tipo de objeto en Blender como mallas, luces o cámaras se compone de dos partes: Object: Almacena información sobre la posición, rotación y el tamaño de un elemento en particular. Object Data: Guarda información sobre otras características, por ejemplo en el caso de mallas, se almacena información sobre geometría, su lista de materiales o grupos de vértices. Cada Object tiene asociado un único Object Data, pero este puede ser el mismo para varios objetos. 3.2. Materiales en Blender Como se explicaba en la sección 2.3, los materiales son aquellos elementos que proporcionan información sobre el aspecto que tiene la superficie de los objetos de la escena. La manera en la que se representa un material puede variar ampliamente entre las diferentes herramientas o plataformas, por lo que vamos a analizar cómo se presentan en Blender y, más adelante, en Unity. En Blender los materiales 3 están compuestos por diferentes “capas” de shaders que definen la apariencia final del objeto. Estos shaders se describen a continuación y se puede observar una representación de los mismos en la figura 3.1. Surface Shader: Controla las texturas y la interacción de la luz en la super- ficie de la malla. Volume Shader: Define el interior de la malla. Ejemplos de uso de este shader son casos como materiales de humo y fuego. Displacement: Se utiliza para alterar la forma de la superficie y el volumen de esta. Un caso común es su uso en materiales como olas de mar. 2https://docs.blender.org/manual/es/2.90/scene_layout/object/introduction.html 3https://docs.blender.org/manual/en/latest/render/materials/introduction.html# physically-based-shading https://docs.blender.org/manual/es/2.90/scene_layout/object/introduction.html https://docs.blender.org/manual/en/latest/render/materials/introduction.html##physically-based-shading https://docs.blender.org/manual/en/latest/render/materials/introduction.html##physically-based-shading 3.2. Materiales en Blender 41 Figura 3.1: Shaders que componen los materiales de Blender. Fuente: Manual de Blender Nuestro proyecto alcanza únicamente a interpretar la capa Surface del material; por este motivo nos enfocaremos únicamente en los efectos que provoca la modifi- cación de esta entrada. Esto se debe a que la entrada Surface es la más común y fundamental en la mayoría de procesos de creación de materiales, controlando aspec- tos como la textura, el color y la reflectividad de la superficie del objeto. Además, los usuarios suelen estar más interesados en la apariencia superficial de los objetos que en efectos más avanzados como el desplazamiento o los materiales volumétricos. Los shaders de Blender internamente se escriben haciendo uso del lenguaje GLSL (sección 2.4.2). Sin embargo, como Blender es un software enfocado a usuarios es- pecializados en disciplinas más artísticas que tecnológicas, es especialmente impor- tante que la interfaz y las herramientas que proporciona sean claras, intuitivas, y abstraigan en la mayor medida posible el aspecto técnico que hay tras los materiales, mostrando la información al usuario de manera que sea fácilmente comprensible sin tener conocimientos de programación o tecnologías de renderizado. Por ello utilizan la programación visual (véase 2.4.2), que simplifica el proceso de creación de estos shaders para adaptarse a todo tipo de usuarios. 3.2.1. Creación de materiales El objetivo de esta sección es mostrar cómo el usuario de Blender trabaja los materiales en la interfaz del programa, para ayudar a la comprensión del funciona- miento de un material. En la figura 3.2 se puede observar la interfaz de Blender, donde el usuario está trabajando una escena que contiene un fondo morado y una esfera que por ahora no tiene ningún material asociado: toma valores por defecto de la escena. En el caso de querer modificar la apariencia de esta esfera con un material, el usuario deberá abrir la pestaña “Material” (figura 3.3) en las propiedades del modelo, donde podrá decidir si asociar un material ya existente, o crear uno nuevo. Sea cual sea su elección, para visualizar en detalle el material creado, es necesario https://docs.blender.org/manual/en/latest/render/materials/introduction.html##physically-based-shading https://docs.blender.org/manual/en/latest/render/materials/introduction.html##physically-based-shading 42 Capítulo 3. Blender Figura 3.2: Vista de la interfaz de Blender. Figura 3.3: Añadir o crear un material en Blender. que despliegue la pestaña “Shading” de entre las opciones de edición. En esta nueva sección Shading (figura 3.4) vamos a centrarnos especialmente en la zona que se puede observar por defecto en la parte inferior y central de la pantalla. Esta zona representa las propiedades del material en forma de nodos. Los nodos son la unidad fundamental para la creación de materiales en Blender. Cada tipo de nodo desempeña una tarea específica y devuelve una salida que permite pasar su información a otros nodos; esta vez, en forma de entrada. Como se puede observar en la figura 3.5, el encadenamiento de nodos produce una especie de esquema o grafo de dependencias que desemboca en una única salida, el nodo “Material Output”, y determina cómo se verá el material al ser asociado a cualquier figura geométrica. Finalmente podemos concluir que cada nodo actúa como una función, que recibe una serie de parámetros y genera una salida que se puede utilizar para alimentar otros nodos y dar el aspecto deseado al objeto. Cada arista representa una depen- dencia entre dichos nodos, donde la salida de un nodo puede adoptarse como un valor de entrada para otro. 3.2. Materiales en Blender 43 Figura 3.4: Pestaña de edición de materiales. Blender en su versión 3.6 cuenta con aproximadamente cien nodos de material diferentes, siendo la mayoría configurables con una serie de parámetros. Esto da lugar a una cantidad de combinaciones posibles, y por lo tanto resultados visuales diferentes, inmensa. Por este motivo, no entraremos a analizar todos los nodos a nivel técnico, sino que ilustraremos a continuación el funcionamiento de uno de los nodos más relevantes, y sin duda el más utilizado: el nodo Principled BSDF. Figura 3.5: Conexiones entre los nodos de un material. 44 Capítulo 3. Blender 3.2.2. Nodo Principled BSDF El nodo Principled BSDF es el nodo que se incluye por defecto al crear un nuevo material en Blender. Se fundamenta en el renderizado basado en físicas (sección 2.3.5), de manera que supone la base de todos los materiales que se representan de forma realista (Sbalu, 2023). Figura 3.6: Nodo Principled BSDF Los parámetros de este nodo son similares a los mencionados en el final de la sección 2.3.3. Los parámetros más utilizados a la hora de crear materiales realistas son: Base Color: Esta entrada suele ser el punto de partida de creación del mate- rial y controla el color de la superficie. Metallic: Esta entrada contiene un valor entre 0 y 1, donde 0 es un material no metálico y 1 se refiere a un material totalmente metálico. Cuando el valor metálico se establece en 1, el material tendrá una apariencia reflectante, similar a la del metal pulido. Cuando el valor se establece en 0, el material aparecerá opaco y no reflectante. Roughness: Esta entrada controla cuán áspera o lisa es la superficie del mate- rial. Es un valor entre 0 y 1, donde 0 significa que la superficie es perfectamente lisa y 1 significa que la superficie es extremadamente áspera. Normal: Esta entrada controla las normales de la superficie del material, lo que permite la creación de detalles de la superficie como golpes, rasguños y otras imperfecciones. La entrada normal toma un mapa de textura, que especifica la dirección de las normales en cada punto de la superficie. 3.3. Motores de Renderizado 45 Alpha: Controla la transparencia del material de manera que, cuanto menor sea su valor, mayor será la transparencia del objeto. Figura 3.7: Materiales realistas creados haciendo uso del nodo Principled BSDF. Fuente: Sbalu (2023) En nuestro proyecto hemos elegido tener en cuenta también otros parámetros relacionados con la superficie del material como son: Emission: Este parámetro permite modificar el color de la luz emitida por la superficie. Emission Strength: Permite cambiar la intensidad de la luz emitida. Para los materiales, un valor de 1.0 asegurará que el objeto en la imagen tenga el mismo color que la entrada de color de emisión, simulando así que no tiene sombras. Figura 3.8: Intensidad de emisión de 0.0 a 10.0. Fuente: Manual de Blender Este es sólo uno de los nodos que se pueden utilizar en Blender para representar materiales. Más adelante, en el capítulo 6, se describen el resto de nodos que se han implementado en la herramienta, junto con explicaciones de cómo se han adaptado e interpretado para su correcta exportación. 3.3. Motores de Renderizado Al tratarse de un software que permite crear elementos en tres dimensiones, Blender requiere de un motor de renderizado para obtener una representación de los modelos, materiales, configuración de iluminación o animaciones en un formato que pueda ser visualizado en la pantalla del usuario o exportado como una imagen o un https://docs.blender.org/manual/en/latest/render/shader_nodes/shader/principled.html 46 Capítulo 3. Blender vídeo. Esta representación se puede obtener de maneras diferentes, dependiendo del motor de renderizado que se seleccione. Hasta la versión 3.6 de Blender, las opciones entre las que se permite seleccionar el motor de renderizado son Cycles, Eevee y Workbench. Este último está enfocado para usarse en la creación de prototipos y bocetos rápidos, ofreciendo una repre- sentación simplificada de los modelos sin aplicar efectos avanzados de iluminación o sombreado. No entraremos en analizarlo con más profundidad debido a su enfoque más limitado. Sin embargo, los otros dos, Cycles y Eevee, resultan interesantes para analizar su funcionamiento y cómo tratan los materiales de la escena. Ambos ofrecen características y ventajas distintas, las cuales se analizan a continuación. 3.3.1. Cycles La documentación de Blender define Cycles como un motor basado en físicas para producción. Como se definió en la sección 2.3.5, las técnicas de renderizado basadas en física tratan de simular la realidad utilizando principios físicos para modelar la interacción entre la luz y el material. Aunque este enfoque pueda parecer el más obvio para efectuar el renderizado, sólo ha sido utilizado en la práctica desde los últimos 15 años aproximadamente (Matt Pharr y Humphreys, 2023). Al estar basado en física, los resultados que podemos obtener serán más realistas, pero vamos a analizar en mayor profundidad su funcionamiento. El motor de renderizado Cycles utiliza ray tracing (sección 2.2.1) para intentar imitar el comportamiento real de una superficie lo más fielmente posible (Valenza y Yamanoor, 2015). En Cycles, los materiales que describen propiedades físicas de superficie tienen una función de distribución de dispersión bidireccional (sección 2.3.5) puesto que es la fórmula que utilizan trazadores de rayos para calcular la representación de un objeto en un entorno virtual. A nivel práctico, esta función se asigna mediante la definición del nodo Principled BSDF, explicado en la sección 3.2.2. Por esta razón, un trazador de rayos puro como Cycles puede representar en tiempos razonables un objeto en un entorno abierto. Los tiempos de representación aumentan considerablemente para espacios cerrados, por ejemplo al representar mue- bles dentro de una habitación, porque los rayos de luz pueden rebotar en el suelo, el techo y las paredes muchas veces antes de llegar a una o varias fuentes de luz. Debido a su alta precisión, Cycles es popular para trabajos que requieren renders de alta calidad y elevado nivel de realismo. Se utiliza especialmente en proyectos que requieren representar entornos o imágenes fotorrealistas, con una precisión que se asemeje lo más posible a la realidad (Shrestha, 2023). Ejemplos de casos de uso que demandan estas características son la generación de contenido publicitario o promoción de productos, así como la generación de contenido para películas o series de televisión. 3.3. Motores de Renderizado 47 3.3.2. Eevee Eevee (Extra Easy Virtual Environment Engine) se introdujo en la versión 2.8 de Blender, como un motor de renderizado basado en objetos (capítulo 2.2.2). El proceso de rasterización hace una estimación de la manera en que la luz interactua con los objetos y materiales haciendo uso de diversos algoritmos. Al ser una esti- mación, será siempre menos realista que un resultado conseguido por otros métodos como ray tracing. Es un motor que está diseñado para producir renders en tiempo real y es espe- cialmente adecuado para tareas que requieren interactividad y velocidad en la vista previa de escenas 3D. Para mostrar los elementos de la escena Eevee admite una amplia variedad de tipos de shaders, incluidos aquellos basados en física (sección 2.3.5) para materiales realistas y permite el uso de materiales que contengan el nodo BSDF anteriormen- te mencionado. Sin embargo, los resultados que proporciona no son equiparables a aquellos generados por Cycles cuando se trata de fotorealismo, debido a que no uti- liza el renderizado basado en luz, y por ello se suele utilizar para simular materiales menos realistas y más estilizados. Al estar diseñado para producir renders en tiempo real, lo que ayuda a la velo- cidad en la vista previa de escenas 3D, Eevee es una elección popular para tareas como la creación de contenido de videojuegos (Cookie, 2018), ya que los desarro- lladores pueden utilizar Eevee para previsualizar los elementos diseñados en tiempo real. Esto agiliza el proceso de desarrollo al permitir iteraciones rápidas. 3.3.3. Similitudes y diferencias entre Eevee y Cycles Tanto Eevee como Cycles utilizan shaders para simular la apariencia de los ma- teriales y la iluminación en la escena. La principal diferencia entre estos dos motores de renderizado se reduce al uso de ray tracing contra el uso de rasterización. Dado que Eevee no utiliza técnicas de renderizado basadas en luz, los resultados que muestra son aproximaciones que no tienen un gran nivel de detalle, al contrario que Cycles. La representación que Cycles ofrece es altamente realista y precisa, pero su pro- ceso de renderizado puede ser considerablemente más lento, lo que no sería ideal para la previsualización rápida y ágil que se suele requerir el desarrollo de video- juegos, puesto que se necesita una respuesta lo más inmediata posible para ajustar elementos visuales. Además, los videojuegos no siempre requieren de una representa- ción visual completamente realista como quizás sí que se requeriría en otros campos como pueden ser los efectos especiales para cine. A pesar de que Eevee puede no proporcionar el mismo nivel de realismo que Cycles, sus capacidades para generar renders rápidos y previsualizar las escenas en tiempo real son aspectos clave que se 48 Capítulo 3. Blender alinean de manera adecuada con las necesidades de nuestro proyecto. Cuando se define un material BSDF, como el que se observa en la figura 3.6, en Blender, se determinan las propiedades del material, y el motor se encargará de simular el comportamiento de la luz al incidir en él. Gracias a la interfaz que proporciona Blender los usuarios no necesitan hacer una distinción a la hora de crear los materiales para las distintas opciones de motor de renderizado, sino que el mismo material es válido para ambas. Únicamente existirá la diferencia de que se percibirán resultados con mayor o menor grado de similitud, dependiendo del caso concreto, y que serán generados a una mayor o menor velocidad según la opción de motor de renderizado seleccionada (figura 3.9). (a) Escena renderizada con Cycles. (b) Escena renderizada con Eevee. Figura 3.9: Ejemplos de dos renderizados diferentes de una misma escena en Blender. 3.4. Add-ons en Blender Un add-on en Blender es un script que extiende las funcionalidades de Blender. Blender cuenta con algunos add-ons pre-instalados, listos para ser activados desde la interfaz, en el menú de preferencias. Es posible añadir tus propios add-ons, o cualquier otro add-on interesante que otros desarrolladores hayan creado y publicado para su uso. Estos add-ons se implementan utilizando Python como lenguaje de programación. Desde el punto de vista del código, la estructura de un add-on en Python sigue un patrón común con varias partes esenciales para todas las creaciones. En primer lugar, se deben definir los metadatos del add-on, que incluyen de- talles como el nombre, el autor, la versión y una breve descripción de sus funcionalidades. Se deben registrar las clases y funciones que amplían las capacidades de Blen- der, como pueden ser crear un nuevo panel o un operador en la interfaz. Los paneles y operadores permiten la interacción del usuario con el add-on a tra- vés de la interfaz de Blender. Estas clases y funciones se registran utilizando bl_info. 3.4. Add-ons en Blender 49 Es necesario registrar el add-on en Blender para que este lo cargue correcta- mente. Por último, se deben implementar las funcionalidades específicas del add-on según los objetivos del proyecto. En Blender, la API bpy proporciona herramientas para manipular y acceder a los materiales y nodos de material directamente desde Python. Conclusiones Tras analizar las características y usos frecuentes de los motores de renderizado disponibles en Blender, tanto Cycles como Eevee, podemos concluir que la elección más adecuada para enfocar este proyecto sería el motor Eevee. Esto quiere decir que Eevee será el motor que deberá utilizar el usuario para que los resultados que observe al crear materiales en Blender coincidan con el resultado generado por la herramienta. Además, al desarrollar la herramienta y exponer este proceso en los próximos capítulos, se tendrá en cuenta el proceso de creación de un add-on en Blender, junto con su API especializada para ello. Capı́tulo 4 Unity Unity1 es un motor de videojuegos que proporciona un entorno de edición donde se pueden vincular y manipular código y recursos visuales y auditivos, así como un conjunto de herramientas para manejar funcionalidades básicas como la física del movimiento o procesos de compilación específicos de la plataforma. El editor de Unity está disponible en macOS, Windows y Linux, e incorpora, entre otras cosas, un entorno de edición 2D/3D, un gestor de assets, un potente motor de renderizado y un sistema de scripting flexible basado en C# . Tiene integraciones que alcanzan desde herramientas de modelado hasta control de versiones, y también cuenta con una tienda de assets incorporada que ofrece recursos para utilizar en los proyectos. En este capítulo se expone el funcionamiento de Unity desde el punto de vista de motor de renderizado, explicando de qué formas se puede realizar el proceso de renderizado en esta plataforma, con el objetivo de seleccionar una que se manten- drá durante el desarrollo del proyecto. También se explica el funcionamiento de los objetos y materiales en Unity, para ayudar a comprender cómo se representa una escena en este motor. 4.1. Renderizado en Unity Antes de 2017, Unity tenía un único pipeline de renderizado. Esto causaba pro- blemas, pues diferentes proyectos no tienen los mismos requisitos de visualización o renderizado. Además, la implementación no era pública para los usuarios que no tuvieran licencia para visualizar el código fuente de Unity, complicando la compren- sión del funcionamiento para los desarrolladores. Por estos motivos, Unity decidió implementar las Scriptable Render Pipelines o tuberías de renderizado programa- bles, para así proporcionar a los desarrolladores más control sobre sus proyectos, 1https://unity.com/es 51 https://unity.com/es 52 Capítulo 4. Unity permitiéndoles añadir o quitar etapas del bucle de renderizado según lo requieran (Ilett, 2022). Esto permite a los desarrolladores adaptar el proceso de renderizado a sus necesidades, y crear efectos visuales que pueden no estar disponibles en las pipelines prediseñadas. Sin embargo, hay que tener en cuenta que este proceso es considerablemente complejo y requiere un conocimiento profundo de los sistemas de renderizado en Unity, y no es una opción que muchos desarrolladores lleven a cabo en sus proyectos, por lo que no profundizaremos en la creación de render pipelines personalizadas. Puesto que la mayoría de usuarios no desean desarrollar una render pipeline desde cero para sus proyectos, Unity proporciona dos plantillas: la High-Definition Render Pipeline (HDRP) y la Universal Render Pipeline (URP) (Sacco, 2023). La versión anterior a estos sistemas de renderizado programables, conocida ante- riormente como Legacy, sigue estando disponible con el nombre de Built-In Render Pipeline, pero su uso está limitado a proyectos de menor complejidad visual o que fueron creados previamente a la introducción de las nuevas render pipelines. A continuación se analizan las características de URP y HDRP, creando una comparativa que justifique la elección del uso de un render pipeline frente al otro en este proyecto. 4.1.1. Universal Render Pipeline La Universal Render Pipeline o URP es un pipeline de renderizado configura- ble preconstruido, creado por Unity. URP2 proporciona flujos de trabajo amigables para los artistas que permiten crear gráficos optimizados de manera rápida y sen- cilla en una variedad de plataformas, desde dispositivos móviles hasta consolas de alta gama y PCs. La URP está diseñada para ofrecer flexibilidad y eficiencia en la representación gráfica en tiempo real en Unity. El bucle de renderizado en la Universal Render Pipeline se puede observar en la figura 4.1. URP se basa en la rasterización, de manera que su modo de renderizado está basado en objetos, como se explicaba en el capítulo 2.2.2. Utiliza shaders avanzados para calcular la iluminación en cada píxel de la pantalla, con vertex shaders y frag- ment shaders que calculan los efectos de la luz y sombras en los objetos. Por ello, soporta la creación de materiales con propiedades físicas realistas, como reflexiones, refracciones y metalicidad. Gracias a estos shaders, se evita tener que recurrir a tec- nologías más costosas como puede ser el ray tracing, a cambio de una representación ligeramente menos fotorrealista. URP ofrece un equilibrio entre rendimiento y calidad visual al representar escenas 2https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/ manual/rendering-in-universalrp.html https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/manual/rendering-in-universalrp.html https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/manual/rendering-in-universalrp.html 4.2. Objetos en Unity 53 Figura 4.1: Bucle de renderizado en URP. Fuente: Documentación de Unity de manera eficiente para una amplia gama de plataformas, incluyendo dispositivos móviles y consolas de videojuegos. 4.1.2. High Definition Render Pipeline La High Definition Render Pipeline o HDRP, permite crear gráficos de última generación y de alta calidad en plataformas de alto rendimiento. HDRP incluye soporte para utilizar la tecnología de ray tracing en Unity. Utiliza shaders altamente sofisticados que permiten un mayor nivel de detalle en la simulación de la luz y los materiales. Con ello se centra en lograr una renderización de alta calidad, lo que significa que presta especial atención a la precisión en la simulación de la luz y la representación de los materiales. Esto incluye efectos de iluminación global, sombras en tiempo real y reflejos. HDRP es compatible con materiales basados en la física (PBR) que simulan cómo la luz interactúa con los objetos de una manera realista. Esto incluye propiedades como el metalizado, la rugosidad, la reflexión y la refracción. Sin embargo, HDRP no es compatible con una amplia gama de dispositivos, como pueden ser dispositivos móviles o múltiples consolas de videojuegos. 4.2. Objetos en Unity Los objetos en Unity son elementos cruciales en la construcción y representación de escenas, tal y como se venía contando en el capítulo 2. https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/manual/rendering-in-universalrp.html 54 Capítulo 4. Unity El primer concepto que se debe conocer es el de GameObject. Este es el concepto más importante en el entorno de Unity, pues representa cualquier elemento presente en la escena: desde la cámara hasta las diferentes luces u objetos con geometría. Sin embargo, un GameObject por sí mismo no tiene ninguna funcionalidad, sino que esta se le es proporcionada por sus componentes. Estos componentes son asociados al GameObject por el usuario, y aportan cada uno una funcionalidad diferente. En la figura 4.2 puede observarse un ejemplo de GameObject con los componentes Trans- form, Mesh Filter, Mesh Renderer y Box Collider asociados, los cuales le permiten ser renderizado en la escena en forma de cubo. Figura 4.2: Un GameObject de Unity con varios componentes asociados. Fuente: Documentación de Unity También es necesario conocer el concepto asset. En Unity, un asset es cualquier recurso que puede ser utilizado en la creación de un proyecto. Varía desde un mo- delo 3D, hasta un archivo de audio o un script para definir el comportamiento de un personaje. Un GameObject por sí mismo no se considera un asset, pues se refiere únicamente al elemento de la escena, y no es algo que se guarde o exista indepen- dientemente de la escena donde se ha creado. Para poder instanciar un GameObject repetidas veces en una o varias escenas, existen lo que se conoce como prefabs. Un prefab en Unity es un tipo de asset que permite encapsular un GameObject. Esencialmente, actúa como una plantilla para un GameObject que puede ser instanciada múltiples veces en las escenas. Al crear un prefab, se captura el estado y configuración de las características que compo- nen el GameObject en un archivo de extensión .prefab, lo que permite su posterior instanciación en cualquier parte del proyecto. Al encapsular elementos complejos en prefabs, se promueve la modularidad y la consistencia en el diseño y creación de la escena. Además, cualquier modificación realizada en el prefab original se reflejará automáticamente en todas sus instancias, https://docs.unity3d.com/Manual/GameObjects.html 4.3. Materiales en Unity 55 lo que facilita la actualización y mantenimiento de la escena en su conjunto. Por este motivo, la herramienta que se desarrolle para este proyecto generará los objetos de Blender en forma de prefab, facilitando su uso y visualización en Unity. 4.3. Materiales en Unity En Unity, los materiales están fuertemente vinculados a los shaders, ya que un material (.mat en Unity) siempre contiene una referencia a un objeto shader que defina sus propiedades. El archivo .mat actúa como un “envoltorio” para el shader y requiere la asignación de sus propiedades correspondientes (colores, texturas, valores numéricos, etc) y la referencia al propio shader. 4.3.1. Shaders en Unity En la sección 2.4.1 se exponía la definición de shader en el contexto del pipeline de renderizado y la GPU. Sin embargo, cuando hablamos de shaders en Unity, estamos haciendo una sobrecarga de este término, pues lo que se conoce como shader en Unity es realmente un archivo de texto con extensión .shader. Los shaders de Unity se escriben en un lenguaje declarativo llamado ShaderLab. Este lenguaje proporciona una forma estructurada de definir las propiedades del ma- terial, como su color, textura, o brillo, que luego se pueden modificar en el inspector de Unity. También permite definir que modos de blending usar y mucho más. En ShaderLab, el código del shader se puede escribir o en HLSL o en Cg (sección 2.4.2) y se estructura en bloques. Anteriormente Unity se basaba en Cg para escribir sus shaders, y aunque a día de hoy aún admite su uso, se recomienda utilizar el lenguaje HLSL. La principal diferencia entre HLSL y Cg es que el código en Cg esta obsoleto y viene con varios archivos de inclusión de shaders built-in por defecto en Unity, lo cual puede ser útil si se necesita esta funcionalidad. Sin embargo, estos archivos de inclusión solo son compatibles con el Built-in Render Pipeline. Por otro lado, el código en HLSL es más reciente y no incluye automáticamente los archivos de inclusión de shaders built-in en Unity, por lo que es necesario incluir manualmente cualquier librería que se desee utilizar. Además, HLSL es compatible con cualquier pipeline de renderizado en Unity (Pai, 2016). A continuación se proporciona un ejemplo de archivo .shader, y se explican las principales características o secciones que lo componen para ayudar a mejorar su comprensión. Shader "Custom/ColorShader" { 56 Capítulo 4. Unity Properties { _MainColor("Main Color", Color) = (1.0, 0.0, 0.0, 1.0) _Intensity("Intensity", float) = 1.0 } SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline"} LOD 100 Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { // Datos de entrada en el vertex shader }; struct v2f { // Datos que se calculan en el vertex shader y se usan en el fragment shader }; v2f vert(appdata v) { // Vertex shader } float4 _MainColor; float _Intensity; float4 frag (v2f i) : SV_Target { float4 outputColor = _MainColor * _Intensity; return outputColor; } ENDHLSL } UsePass "Legacy Shaders/VertexLit/SHADOWCASTER" 4.3. Materiales en Unity 57 } } Este ejemplo de archivo .shader de Unity llamado “ColorShader” permite ajustar el color principal y la intensidad de un objeto. Las principales secciones que se deben comprender y ajustar a la hora de crear un shader personalizado, son las siguientes: Properties: Las propiedades definidas en esta sección son los parámetros que el usuario puede editar desde el inspector de Unity cuando cree un material que utilice este .shader. En este caso de ejemplo, las propiedades son _MainColor y _Intensity. Cada propiedad tiene un nombre que se usa como identificador en el shader, un nombre para identificarlo en el editor de Unity, un tipo de datos (como Color o float), y un valor predeterminado. Tags: Los tags son información que describe cómo se debe renderizar el objeto en términos de su tipo de renderizado y qué pipeline de renderizado debe utilizar. En este caso, el objeto se define como “Opaque” y se especifica que debe usar la Universal Render Pipeline. Estos valores se deben ajustar cuando cambien propiedades como la transparencia del material. Pass: Un pass define un conjunto de operaciones en el pipeline de rende- rizado. En este caso y en todos los que se verán durante este proyecto, se definen únicamente dos pass para el material. El segundo de ellos sería Legacy Shaders/VertexLit/SHADOWCASTER, para que las sombras generadas por otros objetos de la escena se proyecten sobre el objeto que tenga este shader aso- ciado. Este pass es predefinido y no se realizarán ajustes adicionales sobre el mismo. Función vert: La función vert corresponde al vertex shader. Transforma los vértices del objeto y almacena los resultados en una estructura v2f que se pasa a las siguientes etapas del shader. En este proyecto no realizaremos shaders que requieran modificar el comportamiento del vertex shader más allá de lo que se acaba de definir. Función frag: Corresponde al fragment shader, donde se calcula el color del fragmento (píxel) basado en los datos pasados desde el vértice. En este caso, simplemente multiplica el color principal por la intensidad para obtener el color de salida. Esta función será la que contendrá las principales operaciones para definir la apariencia del shader durante el desarrollo del proyecto. 4.3.2. Modelos de shaders predefinidos Debemos tener en cuenta que al crear un material por defecto, los parámetros modificables y las diferentes opciones que se muestran varían según el modelo de shader que tenga asociado. Debido a que nuestro trabajo se centra en el uso de 58 Capítulo 4. Unity Universal Render Pipeline, hemos estudiado los diferentes modelos que este bucle de renderizado ofrece. Physically Based Shading (PBS) El sombreado basado en física simula cómo se ven los objetos en la vida real al calcular la cantidad de luz reflejada desde la superficie siguiendo los principios físicos básicos de conservación de la energía, refracción, reflexión y atenuación de la luz. Los shaders que utilizan este modelo por defecto son los tipos Lit. Simple Shading El modelo de sombreado simple es adecuado para imágenes estilizadas o para juegos que se ejecutan en plataformas menos potentes, como móviles. Con este modelo de sombreado, los materiales no son fotorrealistas y no conservan energía como en el caso anterior. Según este modelo, los materiales reflejan la luz difusa y especular, y no hay correlación entre los dos. La cantidad de luz difusa y especular reflejada por los materiales depende de las propiedades que uno seleccione para el material y, por lo tanto, la luz reflejada total puede exceder la luz entrante total. La reflexión especular varía sólo con la dirección de la cámara. Los shaders que utilizan este modelo por defecto son los tipos Simple Lit. Baked Lit Shading Este modelo no cuenta con iluminación en tiempo real. Los materiales pueden recibir luz baked, es decir, que haya sido precalculada en mapas de luces o de Light Probes. Esto genera el efecto de una mayor profundidad en la escena, pero añade un pequeño coste de rendimiento. El único shader que utiliza este modelo por defecto es el tipo URP Baked Lit shader. Shaders sin iluminación Estos shaders por defecto no tienen luces direccionales ni iluminación de tipo baked. Debido a que no hay cálculos de luz, estos shaders se ejecutan más rápido que aquellos que cuentan con iluminación y por ello se suelen utilizar en objetos que no la requieren. URP cuenta con algunos Shaders Unlit. 4.3.3. Propiedades de los materiales en Unity Una vez conocidos los diferentes tipos de shader que ofrece Unity por defecto, podemos llegar a la conclusión de que cada tipo cuenta con diferentes propieda- des que permiten generar materiales de diversos aspectos. Para conocer mejor estas propiedades nos centraremos en el shader por defecto de URP (URP/Lit), que per- mite representar superficies del mundo real como piedra, madera, vidrio, plástico y metales en calidad fotorrealista. Sus niveles de luz y reflejos son realistas, ya que se trata de un modelo de shader basado en física (Physically Based Shading), y reaccionan correctamente a través de 4.3. Materiales en Unity 59 diversas condiciones de iluminación, por ejemplo, la luz solar o el interior de una cueva oscura. Las propiedades modificables desde el inspector al utilizar este shader en el mate- rial se dividen en las siguientes categorías: opciones de superficie (Surface Options), entradas de superficie (Surface Inputs), detalles de entradas (Detail Inputs) y op- ciones avanzadas (Advanced Options). Debido a que nuestro proyecto se centra en la modificación de las dos primeras, cada uno de sus parámetros se define a conti- nuación. La finalidad de modificar estas propiedades es obtener diferentes versiones de un material. Cambiando estas propiedades se pueden conseguir infinidad de resultados visualmente diferentes, puesto que cada una aporta un valor o característica a la hora de realizar los cálculos del shader. Opciones de superficie Las opciones de superficie controlan la forma en la que la superficie interactúa con la luz para modificar su aspecto al renderizarse en la pantalla. El shader Lit ofrece las siguientes: • Modo de flujo de trabajo (Workflow Mode): Permite elegir un flujo de trabajo ya sea metálico o especular. Una vez elegido, las principales opciones de textura en el resto del inspector ahora seguirán ese flujo de trabajo. ◦ Metálico (Metallic): El shader expone un parámetro “metálico” que indica si el material es metálico o no. ◦ Especular (Specular): Permite controlar el color y la fuerza de los reflejos especulares en el material. • Tipo de superficie (Surface Type): Permite elegir un tipo de super- ficie opaca o transparente para el material. Esto determina en qué render pass renderiza la URP el material. Permite seleccionar entre los siguientes tipos: ◦ Opaco (Opaque): Las superficies opacas siempre son completamente visibles, independientemente de lo que haya detrás de ellas. URP renderiza los materiales opacos primero. ◦ Transparente (Transparent): Los tipos de superficies transparentes se ven afectados por su fondo, y pueden variar según el tipo de superficie transparente que elija. La URP muestra los materiales transparentes en una render pass separada, después de objetos opacos. Esta opción permite elegir el modo de fusión del material. ◦ Modo de fusión (Blending mode): Permite seleccionar la forma en la que Unity calcula el color de cada píxel de un material transparente cuando combina el material con el fondo. Por ejemplo, el modo Nor- mal mezcla los colores del objeto transparente con los del fondo de 60 Capítulo 4. Unity manera estándar, el modo Additive suma los colores y el modo Mul- tiply los multiplica. El modo Alpha Blending utiliza la información del canal alfa para determinar la transparencia. • Caras del renderizado (Render face): Determina qué lados de la geometría se renderizan. La opción de cara frontal (front face) renderiza la parte frontal de la geometría y aplica culling sobre la cara trasera. Este es el modo que se aplica por defecto. La opción de cara trasera, por otra parte, renderiza las caras traseras y aplica culling sobre las delanteras. La tercera opción “both” obliga al URP a renderizar ambas caras de la geometría, y resulta útil para objetos donde vayan a ser visibles ambos lados. • Alpha clipping: Permite al material actuar como un shader de recorte. Se usa para crear un efecto transparente con bordes duros entre las áreas opacas y transparentes, como por ejemplo, para crear hojas de hierba. Para lograr este efecto, URP no muestra valores alfa por debajo del um- bral especificado que acepta valores de 0 a 1 y aparece cuando se habilita Alpha Clipping. • Recibir sombras (Receive shadows): Permite que el objeto de la escena de Unity reciba sombras proyectadas sobre él por otros objetos. Entradas de superficie Las entradas de superficie describen la superficie misma. Por ejemplo, se pue- den usar estas propiedades para que la superficie del objeto parezca húmeda, seca, áspera o lisa. Cuenta con las siguientes propiedades: • Base Map: Añade un color o textura a la superficie, también llamada mapa difuso. • Tiling : Es un multiplicador 2D que ajusta la escala de una textura para que encaje en una malla según los ejes U y V (representando el eje U la coordenada horizontal, y el eje V la coordenada vertical). Controla cuántas veces se repite la textura a lo largo de cada eje en la malla. El valor predeterminado es 1, lo que significa que la textura no se repite. Sin embargo, establecer un valor mayor implica que la textura se repetirá más veces a lo largo de la malla. • Offset : El desplazamiento 2D que posiciona la textura en la malla. • Normal Map: Añade un mapa de normales a la superficie. Esto permite añadir detalles como relieves, arañazos u ondas. El mapa de normales coge la luz de ambiente del entorno, simulando cambios en la superficie del objeto para crear la ilusión de relieve y detalles que realmente no existen en la geometría. • Height Map: URP implementa la técnica de mapeo por paralaje para lograr el efecto de oclusión a nivel de superficie. Esto significa que mo- difica la apariencia de la superficie al simular cambios en la elevación, produciendo efectos de relieve y profundidad en el material. Consigue esto al cambiar las áreas visibles de la textura de la superficie. 4.4. Archivos .meta y GUID 61 • Metallic/Specular Map: Muestra una entrada de mapa que determina cómo se ve la superficie metálica. Configurar el mapa especular permite controlar la intensidad, el color y la difusión de los reflejos especulares en la superficie. • Emission: Hace que parezca que la superficie emite luz. Al activarlo aparecen parámetros para asignar un mapa de emisión o un color de emisión. • Occlusion Map: Permite seleccionar un mapa de oclusión. Se utiliza para proporcionar información acerca de qué áreas del modelo deberían recibir una iluminación indirecta alta o baja. 4.4. Archivos .meta y GUID Cuando importamos los assets en Unity, el motor genera un archivo .meta pa- ra cada archivo importado. Estos archivos .meta contienen un identificador único llamado “GUID”, creado por Unity para referenciar internamente cada objeto. Ade- más de almacenar este ID único, los archivos .meta también incluyen información adicional sobre el archivo asociado. Si se cambia el nombre o la ubicación de un archivo en el proyecto, no surgirán errores debido a que, al tener un identificador único, se seguirán manteniendo las referencias internas entre los assets y sus archivos .meta. Esto asegura que el motor pueda mantener la coherencia entre los archivos y sus metadatos asociados, evitando problemas de referencias y pérdida de datos en el proyecto. Estos archivos y el GUID que guardan son de vital importancia para asociar archivos entre sí cuando existe alguna relación entre ellos. Por ejemplo, se explicaba que un archivo de material en Unity siempre contiene una referencia a uno de tipo shader. Esta referencia se hace guardando en el archivo del material el GUID del shader, que se puede encontrar en su archivo .meta. Estos archivos serán de vital importancia en el desarrollo de la herramienta, pues esta se deberá ocupar de asociar entre sí los archivos que genere. Conclusiones Como ya se mencionaba en el capítulo 3, en este proyecto se busca mantener un equilibrio entre un buen rendimiento y calidad visual. Por este motivo, elegimos URP como pipeline de renderizado. Esto implica que los shaders creados serán compatibles únicamente con este pipeline, y no otros como Built-in o HDRP. URP es compatible en una amplia gama de plataformas, incluidos dispositivos móviles, característica que puede ser de vital importancia para los desarrolladores de videojuegos. 62 Capítulo 4. Unity Además, la herramienta que se desarrolle para este proyecto generará los objetos de Blender en forma de prefab, facilitando su uso y visualización en Unity. En los siguientes capítulos, se establecerá el diseño y se explicará la implementa- ción de la herramienta. Será de especial importancia tener en cuenta los conceptos explicados en esta sección, pues la principal tarea realizada por la herramienta con- siste en la generación de archivos para Unity. Capı́tulo 5 Diseño de la herramienta Para llevar a cabo la implementación de la herramienta es necesario en primer lugar conocer las necesidades de los usuarios y los fallos del estado de exportación actual. Una vez analizados esos puntos clave, será posible establecer unos criterios y objetivos que definan el diseño funcional de la herramienta. 5.1. Estado actual de la exportación de materiales de Blender a Unity En esta sección se pretende exponer el estado actual de la exportación de ma- teriales de Blender a Unity, mostrando el proceso que se lleva a cabo comúnmente para utilizar materiales creados en el primer programa, en el segundo. Para poder integrar los elementos creados en Blender en un proyecto de Unity, es necesario examinar los formatos de archivo compatibles entre ambas plataformas. Dos de los formatos más populares en el desarrollo de videojuegos son OBJ y FBX (sección 2.1.1). Estos formatos destacan por su aceptación en una variedad de he- rramientas para desarrollo en 3D, y su capacidad para guardar información valiosa sobre los modelos creados. Puesto que este proyecto está enfocado en la creación y desarrollo de assets para videojuegos, vamos a centrarnos en aquel formato que sea más utilizado por los desarrolladores de este campo. El formato FBX suele ser la opción preferida para ello, principalmente debido a su amplia aceptación y compatibilidad entre ambas plataformas (Unity y Blender) y por todas las ventajas mencionadas en la sección 2.1.1. Para ilustrar el proceso de exportación, vamos a utilizar un ejemplo práctico. En este caso de estudio, un usuario ha creado en Blender un modelo que corresponde a una simple esfera, como la que podemos observar en la figura 5.1. El usuario ha 63 64 Capítulo 5. Diseño de la herramienta decidido usar una Wave Texture (una textura procedural, véase la sección 2.3.4), lo que le proporciona a la superficie un dibujo con ondulaciones, similares a las de una ola. A continuación, pasa el resultado por una modulación de color, para modificar el rango de colores de la superficie, y obtener una esfera con tonalidades verdes. Finalmente, pasa todo por una función BSDF (sección 3.2.2) para que la superficie refleje la luz, como si de un objeto real se tratase. En la figura 5.2 se puede observar el material descrito, en su vista por nodos. Figura 5.1: Objeto con un material asociado en Blender. En este momento, el usuario decide que su producto está terminado, y quiere exportarlo a Unity para integrarlo en su videojuego. La manera en que lo hace es exportando su trabajo en formato FBX, y seguidamente importando el archivo resultante directamente en Unity. Al terminar ese proceso, en Unity ve la imagen de la figura 5.3. El complejo material que el usuario había creado en Blender se ha perdido por completo al ser enviado a Unity debido a las diferencias en la interpretación de los materiales entre ambos programas y los formatos de archivo. Aunque el formato FBX es compatible con Unity, puede no ser capaz de representar completamente la complejidad de los materiales creados en Blender, lo que resulta en una pérdida de información visual durante la exportación. Esta discrepancia en la visualización de los materiales en Blender y Unity es común al exportar modelos con materiales personalizados, y requiere de técnicas como el baking para conservar la apariencia visual deseada en el motor de juego. La aplicación de esta técnica es actualmente el proceso más popular para la exportación de materiales de Blender a Unity. En este contexto, el baking (sección 2.3.4) se utiliza para convertir propiedades visuales complejas, principalmente sombras y luces, en una textura estática que puede ser aplicada al modelo tridimensional. Esto se logra al capturar la información visual de los objetos en una textura de imagen plana, que luego se asigna al modelo como una textura adicional. Durante el proceso de baking, Blender calcula cómo se vería la escena desde diferentes ángulos de vista, generando así una representación 5.1. Estado actual de la exportación de materiales de Blender a Unity 65 Figura 5.2: Material de la figura 5.1, visto por nodos. visual completa del objeto. Esta textura resultante es exportada junto con el modelo. Para realizar este proceso en Blender, los pasos a seguir son los siguientes: 1. En el material de Blender, se añade un nuevo nodo, de tipo Image Texture. Este nodo será el que determine la salida del material. 2. Ahora se debe crear la textura que se asociará al nodo del paso anterior. Recordemos que el diseñador quiere que el resultado visual corresponda a la esfera verde con ondulaciones que podemos ver en la figura 5.1. Para conseguir esto, se selecciona la opción Bake en la pestaña de la derecha, tal y como se ve en la figura 5.4. Esto creará una textura. Es importante destacar que para hacer este proceso de baking tiene que seleccionar Cycles como motor de renderizado, a pesar de que haya estado trabajando con Eevee. Esto se debe a que Eevee no presenta una opción para realizar el baking de las texturas. 3. Antes de exportar de nuevo el FBX, debe asegurarse de que el nodo conectado a la salida de Material Output, es este nuevo nodo de textura, y que la textura asociada a dicho nodo es la generada en el paso anterior. En caso de que el material requiera interacción con la iluminación, que es lo más probable, el nodo de textura se debe acompañar de un nodo Principled BSDF, que tomará la textura en su entrada Base Color, y será este segundo nodo el que se conecte a la salida del material. 4. Llegados a este punto, puede exportar el FBX de su modelo, e importarlo de nuevo en Unity. Es importante que guarde en su dispositivo e importe en 66 Capítulo 5. Diseño de la herramienta Figura 5.3: Resultado de la primera exportación en Unity. Figura 5.4: Interfaz de Blender donde se realiza el proceso de baking. Unity la textura que ha generado en los pasos anteriores, ya que no se hará de manera automática al exportar el FBX. Una vez completado este proceso, veremos el resultado de la figura 5.5. Estos son los pasos que suele seguir el usuario estandar de Blender para utili- zar sus creaciones en Unity. Cabe destacar que es posible que para modelos más complejos sea necesario realizar algunos ajustes adicionales. Sin embargo, uno de los inconvenientes que se evidencia desde el principio es la necesidad de repetir el proceso de baking cada vez que se desee realizar un cambio, por pequeño que sea, en los parámetros de alguno de los nodos del material en Blender. Estos cambios se podrían querer hacer para ajustar parámetros como el color, nivel de luminosidad o cantidad de ruido en una textura generada proceduralmente, por citar algunos ejem- plos. Además, se debe repetir todo este proceso no solo para cada material distinto, sino para cada modelo diferente aunque que usen el mismo material. 5.2. Descripción funcional de la herramienta 67 Figura 5.5: Resultado tras el proceso de baking en Unity. Finalmente, las propiedades procedurales o que dependen de factores dinámicos pueden no traducirse adecuadamente en una textura generada, lo que resulta en una pérdida de coherencia visual o en la incapacidad de replicar determinados efectos en Unity. Con todo esto lo que se quiere concluir es que este proceso no es trivial ni mucho menos rápido. Por ello, surge la necesidad de crear un exportador que analice el material desde el contexto de Blender, observando uno por uno sus nodos, para convertirlos en un material en un formato específico para Unity, necesidad que va a ser atendida en este proyecto. 5.2. Descripción funcional de la herramienta En primer lugar fue necesario establecer un objetivo definido para el proyecto que se deseaba desarrollar, partiendo del estado de exportación actual expuesto anteriormente. Es fundamental recordar que el objetivo principal de este proyecto es facilitar el proceso de exportación de materiales desde Blender hacia Unity, incluyendo la exportación en forma de materiales y shaders compatibles con Unity, sin la necesidad de realizar procesos de baking adicionales. Dado que el propósito es simplificar el flujo de trabajo entre Blender y Unity, es crucial que la herramienta desarrollada se integre adecuadamente en los espacios de trabajo existentes. Para alcanzar este objetivo, se determinó que el enfoque más apropiado es im- plementar un add-on para Blender. Esta elección se justifca por la facilidad de integración que proporciona Blender a través de su API para el desarrollo de código adicional sobre la plataforma. Además, los usuarios de Blender están familiarizados con el uso de add-ons, lo que facilita la adopción de la herramienta sin interrumpir su flujo de trabajo habitual. El add-on, desarrollado en Python, es el responsable de generar todos los archivos necesarios para representar el material correspondiente en Unity, incluyendo shaders (sección 4.3.1) y archivos .meta (sección 4.4). 68 Capítulo 5. Diseño de la herramienta Es necesario proporcionar más detalles sobre el comportamiento del add-on antes de describir su implementación en detalle, ya que existen varios enfoques posibles. Como se mencionó en el capítulo 2, en el contexto del renderizado las escenas contie- nen objetos con materiales asociados, los cuales junto con la geometría conforman la representación visual. Por lo tanto, el add-on podría exportar únicamente los materiales, o el objeto completo con su geometría. Además, debe especificarse si se exportará solo un objeto individual o la escena al completo con todos sus elementos. Los objetivos concretos establecidos para el proyecto son los siguientes: la he- rramienta de Blender se ocupará de exportar el material o materiales asociados a un objeto específico de la escena, aquel que esté seleccionado en el momento de la exportación. Esta exportación implica generar los archivos de material de Unity, archivos de shaders, archivos de textura si es necesario, y todo ello acompañado de sus archivos .meta (sección 4.4). Para que la experiencia del usuario sea óptima, además de generar los archivos mencionados anteriormente, la herramienta también generará el archivo que contiene la geometría del objeto, y un prefab de Unity, que combina la geometría y sus materiales en un único objeto de juego de Unity (sección 4.2). Así, el flujo de trabajo con la herramienta sería el siguiente: Se instalará el add-on en el entorno de Blender, siguiendo las instrucciones para hacerlo que proporciona la documentación del programa. El usuario generará en una escena de Blender uno o varios elementos con materiales asociados a ellos. Estos serán los elementos que querrá obtener en Unity. El proceso de exportación se llevará a cabo de manera transparente para el usuario del add-on. Después de seleccionar el objeto en Blender y activar la ex- portación a través de un botón de la interfaz, el usuario observará una ventana que le permitirá especificar la ubicación de destino para los archivos exporta- dos en Unity. Además en la interfaz se proporcionará un checkbox “Generate Prefab” que permite al usuario generar únicamente el material y su shader co- rrespondiente, o el objeto al completo. En la figura 5.6 se muestra la interfaz del add-on. Una vez completado este proceso, el add-on genera automáticamente todos los archivos necesarios y los guarda en la ubicación especificada. Si en el editor de Unity arrastra a la escena el archivo .prefab generado por la herramienta, podrá observar un objeto con la misma geometría y los mismos materiales que componían el objeto que exportó en Blender. Una de las principales características que se busca brindar con la herramienta es la posibilidad de parametrizar los materiales desde Unity. Este aspecto es funda- mental para agilizar el proceso de desarrollo y permitir una mayor flexibilidad en la manipulación de los materiales en tiempo real. 5.2. Descripción funcional de la herramienta 69 Figura 5.6: Interfaz del Add-on en Blender El método tradicional de exportar materiales desde Blender e integrarlos en Unity conlleva la limitación de que todo se fusiona en una única textura, lo que restringe la capacidad de ajustar parámetros de manera independiente una vez se importa a Unity. Sin embargo, mediante el uso del add-on desarrollado, los materiales mantienen su naturaleza parametrizable, lo que permite ajustar sus propiedades y características directamente en Unity, sin necesidad de volver a Blender y repetir el proceso de exportación. Esta capacidad de parametrización no solo agiliza las pruebas y la iteración en el proceso de desarrollo, sino que también otorga una mayor flexibilidad al permitir utilizar los mismos materiales en diferentes objetos de la escena, simplificando así la gestión y mantenimiento del proyecto. A través del editor de Unity, será posible acceder a las características del material que estén dentro del campo de propiedades de ShaderLab, modificándolas desde el mismo editor sin tener que volver a Blender para reiniciar todo el proceso de exportación. 5.2.1. Uso de los nodos de Blender Como soporte para la toma de decisiones en el proceso de diseño de la herra- mienta, concretamente para seleccionar qué nodos se iban a desarrollar para poder ser procesados por la herramienta, se realizó una encuesta utilizando Google Forms como plataforma. Dicha encuesta estaba dirigida a usuarios habituales de Blender, con el objetivo de conocer mejor las prioridades de los artistas, diseñadores y de- sarrolladores de videojuegos en lo referente a la creación de materiales en dicha plataforma. En la encuesta, difundida a través de redes sociales, se enumeran una serie de materiales con características diferentes, de los cuales se pide al encuestado que selec- 70 Capítulo 5. Diseño de la herramienta cione aquellos que con frecuencia utiliza en sus proyectos. Los resultados obtenidos fueron los que se pueden observar en la figura 5.7. Figura 5.7: Respuestas para la pregunta “Seleccione cuáles de los siguientes tipos de materiales utiliza con frecuencia en sus proyectos” en la encuesta realizada. En base a los resultados obtenidos de la encuesta, se llegó a la conclusión de que era fundamental focalizar gran parte del trabajo hacia el desarrollo de nodos que permitan la creación de materiales con texturas de imagen, así como una base sólida para generar materiales opacos y con transparencia. Estos tipos fueron los clasifica- dos como los más utilizados por la mayoría de los encuestados, con 21 respuestas indicando el uso frecuente de materiales con texturas de imagen, 14 respuestas para materiales opacos y 14 respuestas para materiales con transparencia, de un total de 22 encuestados. Además de los tipos de materiales más utilizados, se preguntó a los usuarios por los tipos de nodos de material que utiliza con mayor frecuencia. Se proporcionó una lista de nodos populares de Blender de los cuales el encuestado debía elegir aquellos que con más frecuencia utilizara o valorara más. Los resultados obtenidos se pueden observar en la figura 5.8. Figura 5.8: Respuestas para la pregunta “Seleccione cuáles de los siguientes tipos de nodos de material utiliza más frecuentemente” en la encuesta realizada. Estos resultados sirvieron de ayuda para apoyar la decisión de desarrollar e im- plementar en la herramienta algunos de los nodos más votados, como son Principled BSDF e Image Texture. Aunque no ha sido posible implementar todos los nodos 5.2. Descripción funcional de la herramienta 71 mencionados en la encuesta, nos proporciona información valiosa para conocer las prioridades de los usuarios y plantear el trabajo futuro con una base clara. Así, la lista de nodos aceptados por queda de la siguiente manera: Nodo Value Nodo RGB Nodo Principled BSDF Nodo Mix Nodo Add Shader Nodo Mix Shader Nodo Shader to RGB Nodo Texture Coordinate Nodo Mapping Nodo Image Texture Nodo Checker Nodo Color Ramp Nodo Voronoi Texture Nodo Normal Map Conclusiones Sobre el estado actual de la exportación de materiales de Blender a Unity, pode- mos concluir que cuenta con importantes limitaciones, destacando la falta de flexi- bilidad y la necesidad de repetir el proceso para cualquier cambio que se desee hacer sobre el material. La herramienta desarrollada como add-on de Blender permitirá la exportación de materiales sin necesidad de realizar procesos de baking adicionales, manteniendo su parametrización en Unity. En el siguiente capítulo se expone la manera en que se ha llevado a cabo la implementación de esta herramienta, junto con una descripción detallada de las técnicas utilizadas para hacerlo. Capı́tulo 6 Implementación del add-on Una vez establecido un objetivo claro para la herramienta, es necesario exponer cómo se realizara la tarea de exportación. En esta sección se explicará cómo se ha desarrollado la herramienta en su forma de add-on para Blender. Dicho add-on se ha desarrollado utilizando Python como lenguaje de programación. Como se exponía en el capítulo anterior, el comportamiento del add-on se basa en la generación de una serie de archivos necesarios en Unity para representar ade- cuadamente un objeto en la escena. Dichos archivos, explicados en profundidad en el capítulo 4, serán los siguientes: Archivo .shader. Este archivo contiene el código del shader que define cómo se renderiza el material en Unity. En él se especifican los cálculos de color, iluminación, y otros efectos para que el material sea lo más similar posible al material de Blender. Archivo .material. Contiene la configuración que Unity necesita para aplicar el shader a un objeto específico y renderizarlo correctamente en la escena. Archivos de imagen. Estos archivos corresponden las texturas o imágenes utilizadas por el material en Blender, si es que las hay. Estas imágenes son utilizadas por el shader para aplicar efectos visuales al objeto. Deben ser aso- ciados al material. Archivos .meta. Son aquellos archivos que almacenan información sobre los recursos importados, incluidos los materiales y las texturas. Son esenciales para la correcta gestión y asociación de los recursos entre ellos en Unity. Archivo .prefab. El archivo .prefab generado contendrá una referencia a la geometría del objeto y una referencia al material, lo que permite instanciar el objeto completo en la escena de Unity con su apariencia predefinida. 73 74 Capítulo 6. Implementación del add-on El add-on se ha desarrollado siguiendo un enfoque modular y basado en planti- llas. En primer lugar, se definieron plantillas para los archivos que se deben generar, que actúan como modelos predefinidos para la generación de código. Estas planti- llas incluyen marcadores de posición que se rellenan dinámicamente con los datos específicos que se generan durante el proceso de exportación. También es relevante el nombrado de los ficheros generados. Para la comodidad del usuario, todos los ficheros generados en Unity serán nombrados con el nombre que tuviera el material en el contexto de Blender, seguido de la extensión que precise cada tipo de archivo. Aun así, el nombre que se determina no afecta al comportamiento del shader, del material, ni del objeto generado. Esta estrategia de desarrollo modular y basada en plantillas permite una fácil extensión y personalización del add-on en el futuro. Los desarrolladores pueden agregar nuevas funcionalidades o modificar el comportamiento existente simplemente ajustando las plantillas y añadiendo funciones según sea necesario. De esta manera, el add-on puede adaptarse a diferentes tipos de materiales y requisitos de renderizado sin requerir cambios significativos en su estructura subyacente. A continuación se define un diagrama modular que representa el orden que sigue la herramienta a la hora de realizar el proceso de exportación. Generación de archivos de imagen Generación de .shader Generación de .mat Generación de .fbx Generación de .prefab Los archivos deben generarse siguiendo el orden presentado en el diagrama, ya que existen dependencias entre ellos que requieren resolución para garantizar una aso- ciación adecuada. Estas dependencias se llenan con datos generados en el momento para cada archivo específico, como el identificador único global (GUID, véase en la sección 4.4), que es independiente del objeto particular de Blender que se está pro- cesando. Estos identificadores únicos son esenciales para establecer correctamente la vinculación entre los archivos generados. El shader debe ser creado primero, ya que tanto su identificador como el de las imágenes son necesarios para vincularlos al ar- chivo .material. De manera similar, el archivo .prefab requiere acceso al identificador único de los materiales y su .fbx para su exportación. Con el objetivo de simplificar la explicación del proceso de exportación, se va a centrar la explicación en la exportación de un único material. Sin embargo, es importante recalcar que si el objeto seleccionado para el proceso tiene asociados varios materiales, se repetirá el proceso para cada uno de ellos. 6.1. Generación del archivo .shader 75 6.1. Generación del archivo .shader El flujo de ejecución del add-on implica la extracción de información del material de Blender, que se utiliza para rellenar las plantillas .shader. Al rellenar las planti- llas del shader, el add-on agrega funciones de HLSL (sección 2.4.2), según los datos especificados en el material de Blender. Estas funciones incluyen cálculos de ilumi- nación, sombreado, mezcla de texturas, o cualquier otra operación necesaria para replicar el aspecto del objeto en Unity. La asociación de ficheros, como texturas o mapas de normales, también se identifica y se integra de manera dinámica en las plantillas de shader para asegurar la coherencia entre los elementos del material en Blender y su representación en Unity. En esta sección se explicará el proceso llevado a cabo por la herramienta para obtener este archivo .shader mediante la edición de una plantilla de shader predefinida. La generación de este archivo es la más compleja de entre todos los archivos generados. A continuación se expone un diagrama en el que se refleja, en forma de esquema modular, el proceso de generación del archivo. Copiar el archivo de plantilla Recorrer en profundidad de los nodos conectados a la salida del material Añadir código a la plantilla para cada nodo Añadir operaciones de cutoff y blending Para desarrollar esta herramienta ha sido necesario interactuar con el entorno de Blender desde un programa en Python. Es decir, se ha accedido a los elementos, concretamente los materiales, desde código en vez de desde la interfaz del programa. De esta manera, los primeros pasos que tomará la herramienta serán obtener el material activo, verificar si utiliza nodos y, en caso afirmativo, acceder al árbol de nodos. A continuación y tal y como se puede apreciar en el esquema, itera sobre los nodos, haciendo un recorrido en profundidad de los nodos del material para así visitar cada conexión que define el resultado visual final, como se explicará a continuación. Esta iteración es uno de los puntos clave del programa, durante la cual se procesan las propiedades de cada nodo y se va generando el shader HLSL correspondiente, basado en la configuración de los nodos visitados. Durante la iteración se van agregando secciones específicas al .shader para representar las operaciones que realiza cada nodo y que tienen un efecto visual en el material. Además, se manejan aspectos importantes como los modos de mezcla y umbrales alfa para garantizar que el shader final refleje fielmente las propiedades y aspecto visual del material en Blender, tal y como se explicará en las secciones siguientes. 76 Capítulo 6. Implementación del add-on 6.1.1. Plantilla .shader El proceso de generación de shader comienza con la carga de una plantilla de shader desde un archivo. Sin embargo, la plantilla por sí sola no conforma un shader que compile y ejecute correctamente, pues necesita ser procesada por la herramien- ta de exportación para que esta la modifique. Por lo tanto, se irán realizando las adaptaciones necesarias para personalizarla según las propiedades del material en cuestión. Esta adaptación se realiza añadiendo el código necesario al archivo del shader en forma de cadena de texto con la ayuda de las operaciones de escritura en archivo y operaciones con cadenas de texto en Python. Este proceso se realiza mediante el uso de la biblioteca externa de Python llamada Jinja2, un motor de plantillas que simplifica la creación y modificación de plantillas. Al tratarse de un shader personalizado para Unity, la estructura general de la plantilla sigue la de un shader típico hecho con HLSL y ShaderLab, de manera similar al ejemplo descrito en la sección 4.3.1. En primer lugar se encuentran las propiedades que se podrán modificar desde el editor de Unity. Estas propiedades coinciden en su mayoría con aquellas que se pueden ajustar desde los nodos de material en Blender, aportando al usuario esa flexibilidad que se explicaba anteriormente, al importar los materiales a Unity. Properties { // Add properties } El comentario de código “Add properties” permite localizar esta sección desde el add- on de Blender, de modo que a medida que se van recorriendo los nodos, se pueden analizar sus propiedades y agregarlas a esta sección. Sin embargo, es importante tener en cuenta que los tipos de variables y cómo se guardan sus valores varía entre ShaderLab y la representación interna de Blender. Es por ello que antes de añadir una propiedad se debe realizar una conversión tanto en el nombre de la variable como en cómo se escribe su contenido. Por ejemplo, Blender utiliza Shader para nombrar a un color que incluye su componente alpha. Sin embargo, en ShaderLab estas deben ser nombradas como Color. Recordemos que al trabajar con la herramienta, se opera con cadenas de texto por estar utilizando operaciones de escritura en una plantilla. Esto quiere decir que todas las operaciones se realizan en texto plano que se escribe en un archivo mediante las operaciones proporcionadas por Python. El próximo elemento de la plantilla son los Tags. Los Tags proporcionan infor- mación sobre cómo debe ser renderizado el objeto al que se aplica el shader (sección 4.3.1). Es importante poder modificarlos desde la herramienta pues en Blender se pueden configurar materiales translúcidos o transparentes, característica que se debe indicar en esta sección del .shader. 6.1. Generación del archivo .shader 77 SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline"} // Add tags LOD 100 // Add pass properties // Add culling ... Se utiliza el comentario de código “Add Tags” para localizar y escribir en la plantilla los Tags adicionales que requiera el .shader que se genera. De la misma forma, como se puede observar en el fragmento de código, existen comentarios para localizar y añadir propiedades que pueda requerir el shader relati- vas al pase de renderizado y el culling. Esto será de utilidad en shaders que requieran de algún tipo de transparencia o traslucidez, como se explicará más adelante. Finalmente, dentro del shader HLSL existen comentarios para añadir defines, archivos de inclusión, estructuras, funciones y llamadas a dichas funciones. Pass { HLSLPROGRAM //Add includes // Add defines ... // Add structs // Add variables ... // Add methods float4 frag (v2f i) : SV_Target { // Call methods // Add cutoff 78 Capítulo 6. Implementación del add-on ... } } Esto servirá para modelar el aspecto visual que genera el shader en función de los nodos utilizados en Blender que la herramienta recorre. Veamos entonces el proceso que se realiza para determinar qué funciones, estructuras y definiciones deben ser añadidas a la plantilla. 6.1.2. Recorrido de los nodos El recorrido de nodos del material de Blender se realiza mediante la función iterate_node, la cual emplea un enfoque recursivo para explorar la estructura de nodos del material. Esta función se encarga de procesar cada nodo, analizando sus propiedades y utilizando las información adecuada para escribir el shader final. Las estrategias definidas para el comportamiento específico de cada nodo se explicarán en profundidad en la siguiente sección. El recorrido de los nodos se hace con el uso de la API bpy, explicada en la sección 3.4. Para comenzar a interactuar con los materiales desde Python, primero es necesario obtener el material activo. Esto se puede lograr accediendo al objeto actualmente seleccionado o al objeto activo en la escena y luego accediendo a su material. Por ejemplo, podemos usar bpy.context.object.active_material para obtener el material activo. Una vez tenemos acceso al material activo, podemos verificar si utiliza nodos. Si lo hace, entonces podremos acceder al árbol de nodos del material utilizando material.node_tree. Esto nos proporciona la representación visual de los nodos que componen el material y las conexiones entre ellos, tal y como se explicaba en la sección 3.2. Ahora, para interactuar con los nodos de material, podemos recorrerlos utilizando material.node_tree.nodes. Esto nos permite acceder a cada nodo individualmente para realizar las operaciones que requiera según el tipo de nodo del que se trate. De las diversas acciones que se pueden realizar sobre los nodos, nos serán de especial utilidad el acceso a las propiedades y el acceso a las conexiones existentes entre el los nodos del material. Esto último nos permite recorrer los nodos del material de manera ordenada, tratándolo como un árbol o un grafo. Las conexiones del material de Blender se van visitando con un recorrido en profundidad (DFS por sus siglas en inglés, Depth-First Search), un algoritmo uti- lizado para explorar o recorrer una estructura de datos, como grafos o árboles, de manera sistemática (figura 6.1). En un DFS, el algoritmo comienza en un nodo ini- cial y explora tan profundamente como sea posible a lo largo de cada rama antes de retroceder. Esto es de especial utilidad en la tarea que se debe realizar, pues el 6.1. Generación del archivo .shader 79 recorrido en profundidad garantiza que todos los nodos y conexiones del material se van a visitar. Además, seguir un orden controlado para visitar los nodos es de especial importancia para garantizar la consistencia al generar el shader, puesto que cambiar el orden de los nodos, en la gran mayoría de ocasiones, también cambiará el resultado visual. Por último, al marcar los nodos ya visitados, DFS previene la posibilidad de caer en bucles infinitos o repetir código innecesariamente durante el recorrido del grafo. Figura 6.1: Depth First Search 6.1.3. Instanciación de los nodos en la plantilla El código implementado hace uso del patrón de diseño Strategy para implemen- tar diferentes estrategias de escritura de nodos de material. Este enfoque permite encapsular algoritmos específicos para la generación del .shader, facilitando la reuti- lización del código y la posible ampliación añadiendo nuevas estrategias para nuevos tipos de nodos. La clase Context sirve como el contexto en el que se ejecutan estas estrategias, permitiendo cambiar dinámicamente la estrategia utilizada en tiempo de ejecución, de manera que durante el recorrido de los nodos, se van asociando diferen- tes estrategias basándose en el tipo de nodo que se esté visitando en ese momento. Esto es posible puesto que se proporciona a la herramienta con un diccionario que asocia a cada tipo de nodo el nombre de su estrategia correspondiente. De esta ma- nera, al recorrer los nodos, será posible añadir diferentes funcionalidades al shader dependiendo del tipo específico o características del nodo. Lo primero que hay que determinar es cómo se va a interpretar en el código del shader la información que proporcionan los nodos de material. El enfoque que se ha tomado es similar al que usa Blender de manera interna para guardar la información: utilizando una estructura (struct) para guardar la información que devuelve cada nodo. La herramienta generará un struct para cada tipo de nodo 80 Capítulo 6. Implementación del add-on procesado, añadiendo entradas al mismo en función de sus parámetros de salida, de manera que las entradas del struct coincidirán con los parámetros que devuelve dicho nodo en Blender. Por ejemplo, un nodo de tipo Image Texture tiene dos salidas: Color y Alpha. Su struct tendrá por lo tanto el siguiente aspecto: struct Image_Texture_struct{ float3 Color; float Alpha; }; Es importante que los tipos asociados a cada salida del struct correspondan con un tipo válido en HLSL, por lo que la herramienta realiza modificaciones en el tipo asociado a la salida original de Blender, obteniendo la equivalente en el lenguaje del shader. Además del struct que guarda la información que devuelve cada nodo, es ne- cesario determinar una función que reciba los mismos parámetros que el nodo de material, y complete la información de dicho struct con un resultado o resultados acordes a los de Blender. Es decir, se debe definir en un archivo de texto una función compatible con HLSL que a partir de unos parámetros dados (los cuales también deben estar incluidos en este archivo de texto, y deben coincidir con los paráme- tros que recibe el nodo de material en Blender) realice cálculos para obtener uno o más valores que correspondan a los que devuelve el nodo en su salida o salidas. Esa información calculada se guarda en el struct correspondiente al nodo que se está procesando. Por lo tanto, es trabajo del desarrollador definir una función para cada tipo de nodo que desee que la herramienta pueda procesar. Algunos de los nodos imple- mentados pueden requerir hacer ampliaciones a la plantilla, o son más complejos de replicar en HLSL, por lo que en las siguientes secciones se hará un desglose de los nodos que la herramienta es capaz de procesar en este punto de su desarrollo. El trabajo realizado en esta sección ha consistido en replicar el comportamiento de estos nodos en funciones HLSL. Para algunos nodos esta tarea es sencilla, pu- diéndose tratar de simples operaciones como sumas o restas. En tales casos, no se añadirá información adicional sobre el proceso de desarrollo. Sin embargo, en varios casos más complejos, la implementación del nodo ha requerido aplicar técnicas más elaboradas o la inclusión de archivos propios de Unity para poder lograr una repro- ducción adecuada en el nuevo contexto. Se analizarán detalladamente dichos casos para proporcionar una comprensión más profunda de su funcionamiento. En el siguiente fragmento de código puede observarse un ejemplo de función HLSL definida en un archivo de texto para el nodo Add_Shader. Aquí se puede observar cómo la función toma por parámetros de entrada los mismos parámetros que toma el nodo en Blender, y añade el cálculo resultante a una variable perteneciente a un struct Add_Shader_struct. Este proceso se repetirá de manera similar para el resto de nodos implementados. 6.1. Generación del archivo .shader 81 Add_Shader_struct add_shader(float4 shader1, float4 shader2) { Add_Shader_struct output; output.Shader = shader1 + shader2; output.Shader.w = clamp(output.Shader.w, 0.0, 1.0); return output; } Desde el punto de vista de la programación de la herramienta, para cada uno de estos nodos se define una estrategia dentro del patrón de diseño Strategy tal y como se mencionaba anteriormente. Las estrategias se ocuparán de añadir al shader las funciones, archivos de inclusión o recursos adicionales que necesite el nodo aso- ciado a dicha estrategia, guardando la ruta al archivo que define la función HLSL correspondiente al nodo. A continuación se exponen los nodos de Blender que son compatibles con la he- rramienta en su estado actual de desarrollo, los cuales se enumeraban en la sección 5.2.1. Son los nodos con los que se ha trabajado durante este proyecto para que los materiales que los utilicen se exporten correctamente. Se proporciona la descripción y funcionamiento general de cada nodo en el contexto de Blender, así como las carac- terísticas o funcionalidades adicionales que puedan haber requerido para integrarse adecuadamente en la herramienta. 6.1.3.1. Nodo Value y Nodo RGB Se tratan de dos de los nodos más sencillos que ofrece Blender para la creación de materiales. Ambos se ocupan de recibir un valor, ya sea un valor numérico o un color, y transmitirlo a otro nodo posterior. Figura 6.2: Nodos Value y RGB en Blender Son nodos sencillos tanto a nivel de funcionamiento como a nivel de código, ambos representando funciones que devuelven el mismo valor que reciben por parámetro. 82 Capítulo 6. Implementación del add-on 6.1.3.2. Nodo Principled BSDF El uso del renderizado basado en físicas implica que los materiales creados con el shader Principled BSDF (sección 3.2.2) se pueden exportar a otros programas y motores de juego que soporten este flujo de trabajo sin perder su apariencia o propiedades. En el caso de Unity, se ofrece este soporte a través de funciones incluidas en el paquete de instalación de Universal Render Pipeline que se pueden acceder a tra- vés del archivo Lighting.hlsl. Aunque este archivo ofrece funciones para simular diferentes modelos de iluminación como “Phong lighting” o “Unlit”, en nuestro pro- yecto hemos hecho uso de la función UniversalFragmentPBR. Esta función recibe dos parámetros de tipo struct donde: InputData: Contiene variables con información respecto a la posición global, posición respecto a la cámara, normales y demás datos de entrada necesarios para realizar cálculos posteriormente. SurfaceData: Contiene variables con información respecto a la superficie del objeto como su albedo, su metalicidad, su rugosidad o su propiedad alfa. Figura 6.3: Correspondencia entre parámetros de Principled BSDF en Blender y parámetros de SurfaceData en Unity Como se puede observar en la figura 6.3, muchos de los parámetros que utilizamos 6.1. Generación del archivo .shader 83 en Blender, representan un parámetro similar en el struct SurfaceData usado en el método UniversalFragmentPBR de Unity. Esta similitud entre los atributos, junto a la función proporcionada por Unity en URP, han permitido que la simulación de los materiales realistas en Unity fuera mucho más sencilla debido a que gran parte del código de este nodo se ha podido implementar a base de asignar los parámetros necesarios para SurfaceData, con los parámetros proporcionados por Principled BSDF. De no haber podido utilizar esta función tendríamos que haber realizado los cálculos necesarios para replicar la interacción de la luz con un material de este tipo en Unity. Por lo tanto, esto hubiera supuesto aplicar las fórmulas propias del rende- rizado basado en físicas para simular el comportamiento de luz real al interactuar con objetos. 6.1.3.3. Nodo Mix Como se puede intuir por su nombre, y con la ayuda de la documentación de Blender1, sabemos que el nodo Mix mezcla valores, colores y vectores, usando un fac- tor para controlar la cantidad de interpolación (figura 6.4). Cuando mezcla colores, existen diferentes opciones de mezclado. El nodo Mix en Blender difiere ligeramente en su funcionamiento de la mayoría de otros nodos. Esto es porque los valores que acepta y que devuelve cambian en función de uno de sus parámetros: Data Type. Este es el parámetro que determina si va a mezclar valores, colores o vectores. En el contexto de este nodo, los valores serán números con una precisión de hasta tres decimales. En la figura 6.4 se puede observar como el mismo nodo cambia visualmente de- pendiendo de la opción seleccionada en su primera entrada. Es lógico que el nodo se comporte de esta manera, pues los valores de entrada deben corresponder con el tipo de lo que se quiera mezclar. Desde el punto de vista del código de Blender, este nodo se interpreta de manera conjunta en sus tres versiones. Es decir, se considera un nodo con tres salidas diferentes, de las cuales sólo una puede estar conectada a otro nodo, y varios valores de entrada, de los cuales solo dos serán relevantes para calcular la salida. También se consideran parámetros del nodo los valores Factor y Clamp Factor, que serán comunes independientemente del tipo de datos selecciona- do. Además, existen opciones de mezclado diferentes en los casos de estar mezclando vectores o colores. Para poder adaptar este nodo a la estructura general de la herramienta, fue necesario ampliar ligeramente la estrategia Mix_strategy en comparación con el resto de estrategias. Esto se debe a que, a pesar de ser un mismo nodo, la función HLSL que corresponde a su comportamiento varía en función del tipo de datos seleccionado. Es por ello que la herramienta añade una función u otra según el valor 1https://docs.blender.org/manual/en/3.6/modeling/geometry_nodes/utilities/ math/mix.html https://docs.blender.org/manual/en/3.6/modeling/geometry_nodes/utilities/math/mix.html https://docs.blender.org/manual/en/3.6/modeling/geometry_nodes/utilities/math/mix.html 84 Capítulo 6. Implementación del add-on Figura 6.4: Nodo Mix en sus diferentes configuraciones. Fuente: Manual de Blender de este parámetro. Funciones de mezclado (Blending Functions) Tal y como se indicaba, hay diferentes opciones de mezclado para el nodo Mix con colores, con un total de diecinueve modos diferentes hasta la versión 3.6 de Blender. En su estado actual, la herramienta únicamente acepta dos de estas funciones: Mix y Add. Sin embargo, es posible añadir nuevas funciones de mezclado a la herramienta añadiendo la función HLSL correspondiente. Para añadir las funciones existentes, el proceso ha consistido en analizar el código de Blender, para así entender cómo realiza el propio programa esas operaciones de mezclado de color. A continuación, se han creado funciones compatibles en HLSL que se comporten de la misma manera. 6.1.3.4. Nodo Add Shader Se trata de un nodo cuya finalidad es bastante directa: combinar los resultados de dos shaders para producir un nuevo resultado como resultado de la mezcla de los mismos (figura 6.5). En este contexto, el término shader no se refiere a los shaders como los hemos entendido en apartados anteriores de este documento, sino más bien a resultados visuales representados por valores RGBA (canales rojo, verde, azul y alfa). En el editor de nodos de Blender, y por lo tanto también en este nodo, un shader se refiere a cualquier salida visualizable que puede ser representada por estos valores de color RGBA. Cuando analizamos el código que hay tras este nodo, aprendemos que su funcio- namiento se basa en sumar los valores de cada canal de los parámetros de entrada. Es importante destacar que el nodo asegura que el canal alpha se mantiene en un https://docs.blender.org/manual/en/3.6/modeling/geometry_nodes/utilities/math/mix.html 6.1. Generación del archivo .shader 85 Figura 6.5: Nodo Add Shader. Fuente: Manual de Blender rango válido, entre los valores cero y uno. 6.1.3.5. Nodo Mix Shader Al igual que el nodo anterior, el nodo Mix Shader (figura 6.6) toma como valores de entrada dos shaders, con los cuales realiza una operación. A diferencia de Add Shader, devuelve como resultado una mezcla de los valores de los shaders, interpo- lando entre los dos según el valor de un tercer parámetro llamado Fac (Factor). Si su valor es 0, el resultado será idéntico al primer shader de entrada; si es 1, será idéntico al segundo shader; y si es 0.5, será una mezcla igual de ambos shaders. Este comportamiento se consigue replicar en HLSL gracias a la función lerp2, que realiza una interpolación lineal entre dos valores, utilizando un tercer valor como factor de interpolación. Figura 6.6: Nodo Mix Shader. Fuente: Manual de Blender 6.1.3.6. Nodo Shader to RGB Este nodo tiene una entrada de tipo Shader (RGBA), la cual es dividida en dos salidas: una llamada Color (RGB) y otra Alpha (A), dividiendo los canales de manera que se pueda utilizar la salida del color RGB sin tener en cuenta su propiedad alfa (figura 6.7). Es un nodo que se utiliza típicamente en la renderización no foto realista, para aplicar efectos adicionales en la salida de los nodos Principled BSDF. Por ejemplo, se puede utilizar para crear efectos de Toon Shading o Cell Shading. 2https://learn.microsoft.com/es-es/windows/win32/direct3dhlsl/ dx-graphics-hlsl-lerp https://docs.blender.org/manual/id/3.6/render/shader_nodes/shader/add.html https://docs.blender.org/manual/id/3.6/render/shader_nodes/shader/mix.html https://learn.microsoft.com/es-es/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp https://learn.microsoft.com/es-es/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp 86 Capítulo 6. Implementación del add-on Figura 6.7: Nodo Shader to RGB. 6.1.3.7. Nodo Texture Coordinate Es un nodo que proporciona un vector como valor de salida (figura 6.8). Se utiliza comúnmente para indicar las coordenadas de una textura, por ejemplo, para las coordenadas de texturas de una imagen, o de texturas procedurales. El add-on da soporte para sus salidas “Generated”, “UV” y “Normal”. Este nodo tiene de especial que es un nodo de tipo raíz, es decir, no tiene ningún input a modo de parámetro especificado por el usuario. Una de las dificultades al crear este nodo fue traducir las coordenadas de textura Generated a HLSL. Según la documentación de Blender, estas coordenadas corres- ponden a las posiciones de los vértices normalizadas de 0 a 1 sobre el bounding box de la malla. El bounding box es una caja rectangular que encuadra por completo al objeto, siendo del menor tamaño posible para contener al objeto en todas sus dimensiones. Esta caja puede ser definida por varios atributos: center : El centro del bounding box. extents : Las dimensiones del bounding box. max: El punto máximo de la caja. Se calcula como center + extents. min: El punto mínimo de la caja. Se calcula como center − extents. Estos atributos son fundamentales para definir la ubicación y el tamaño del bounding box, y a su vez, determinan el espacio en el que se normalizan las coordenadas de textura Generated. Vmax las coordenadas del bounding box max Vmin las coordenadas del bounding box min Vv las coordenadas del vértice locales. Vg las coordenadas Generated. 6.1. Generación del archivo .shader 87 Siguiendo esta notación, la fórmula general para calcular las coodrdenadas Generated es: Vg = Vv − Vmin Vmax − Vmin El principal desafío en este caso es la incapacidad de acceder a las dimensiones del bounding box del objeto desde el código HLSL, a diferencia de otros valores del objeto que si son accesibles como sus coordenadas y normales. Para solucionarlo, se añaden los atributos de la bounding box como parámetros de entrada del archivo .shader. Sin embargo, esta solución presenta algunos problemas. En primer lugar, existe la posibilidad de que el usuario modifique estas coordenadas en el editor de Unity, lo que puede resultar en coordenadas Generated incorrectas. Además, si este material se aplica a otro objeto que no comparte el mismo bounding box, las coordenadas Generated no estarán adaptadas al segundo objeto. Más allá de estas posibles com- plicaciones, esta es la única solución que permite mantener la coherencia visual completamente entre el resultado obtenido en Blender y el obtenido en Unity cuan- do se utilice la salida Generated de este nodo, a pesar de limitar la flexibilidad o posible reutilización del material. Para adaptar este nodo a la estructura general de la herramienta, fue necesario ampliar ligeramente la estrategia TextureCoordinate_strategy. Esto se debe a que es un nodo raíz, no tiene sentido llamar a una función a este nodo, porque no habría ningún parámetro de entrada. Este se iguala a las texturas de coordenadas recogidas del vertex shader, con sus respectivas operaciones. Figura 6.8: Nodo Texture Coordinate. 6.1.3.8. Nodo Mapping Nodo utilizado para aplicar trasformaciones (rotación, traslación y escala) a un vector (figura 6.9). Este nodo tiene como input los siguientes parámetros: 88 Capítulo 6. Implementación del add-on Vector : vector que va a ser transformado. Location: vector de translación que se aplica. Rotation: vector de rotación que se aplica. Scale: vector de escala que se aplica. Type: propiedad de tipo enum. El nodo aplica la transformación de diferentes maneras en función de este tipo. Como salida, devuelve el vector transformado. El add-on solo da soporte para el tipo de transformación Point, que corresponde al que viene por defecto al añadir el nodo en Blender. Figura 6.9: Nodo Mapping 6.1.3.9. Nodo Image Texture Nodo utilizado para aplicar una imagen como textura sobre un objeto (figura 6.10). Las propiedades sobre las que la herramienta ofrece soporte son las que vienen 6.1. Generación del archivo .shader 89 por defecto en Blender: interpolación lineal, proyección plana y aplicación de la textura con repetición. Este nodo recibe como input un vector, que corresponde a la coordenada de textura donde debe aplicar la imagen. Esta entrada se determina a través del resultado producido por un nodo Texture Coordinate unido a un nodo Mapping. Figura 6.10: Nodo Image Texture Este nodo tiene como característica especial en su exportación, que la imagen asignada debe ser guardada para poder utilizarse más tarde para el material de Unity. Esto se realiza en el código de la herramienta en el momento que se analiza un nodo de esta categoría. Las texturas empleadas en los materiales de Blender se duplican y se almacena una copia en la ruta destino, dentro de un directorio textures, para ayudar a la organización de los archivos generados, tal y como se explicará más adelante en la sección 6.2. 6.1.3.10. Nodo Checker Nodo que genera una textura procedural que consiste en cuadros alternados de dos colores, de manera similar a un tablero de ajedrez (figura 6.11). Figura 6.11: Nodo Checker Como input recibe un vector a modo de coordenada de textura donde aplicar la 90 Capítulo 6. Implementación del add-on textura generada, un valor numérico Scale, o escala, que se aplica a la textura, y dos colores, Color1 y Color2, que corresponderán a los colores que se apliquen de manera alternada a la textura generada. Devuelve un color, Color, y un Fac. Esto último indica si un punto en las coor- denadas de textura pertenece al cuadro 1 (factor 1) o al otro cuadro (factor 0) del patrón cuadriculado. 6.1.3.11. Nodo Color Ramp Nodo usado para asignar valores a colores mediante el uso de degradados (figura 6.12). Este nodo recibe un único input Factor, que corresponde al valor que se quiere asignar. Si el factor es 0 se elige el color del extremo izquierdo, y si es 1 se selecciona el color del extremo derecho. Las propiedades que el usuario determina para este nodo son los colores a insertar en el degradado y el tipo de interpolación a usar con dichos colores. Para adaptar este nodo a la estructura general de la herramienta, se requirió una ligera expansión de la estrategia Color Ramp_strategy. Esto se debió a que el Color Ramp de Blender puede crear una cantidad variable de colores en tiempo de ejecución, mientras que HLSL no admite la creación de memoria dinámica. Por lo tanto, se accede al número de colores que tiene este nodo y se crea un array de colores RGB con los colores correspondientes, de manera que el usuario no podrá crear más colores en el inspector de Unity, a menos que modifique manualmente el código HLSL. Además, el nodo de Blender recibe qué tipo de interpolación se utiliza. Nuestro nodo da soporte para Linear, modo con el cual se aplica una interpolación lineal a cada color, y Constant, con el cual el método devuelve el color sin ningún tipo de interpolación. Figura 6.12: Nodo Color Ramp 6.1. Generación del archivo .shader 91 6.1.3.12. Nodo Voronoi Texture Nodo que crea una textura procedural de ruído3 (figura 6.13). El algoritmo elige puntos aleatorios en el plano o el espacio y hace una representación de la distancia de dichos puntos a cada píxel. Figura 6.13: Nodo Voronoi Texture Gracias a las propiedades del nodo de Blender, permite que la textura procedural se genere de distintas maneras. Estas propiedades, llamadas Dimensions, son las dimensiones del espacio donde se genera el ruido. Por ejemplo, con 2D se evalúa el ruido en un espacio 2D obviando el eje Z. Por otro lado, en 3D, se evalúa el ruido en un espacio 3D. La propiedad Distance Metric corresponde a la distancia usada para computar esta textura. Puede ser Euclidean, Manhattan, Chebychevy o Minkowski. Por último está la propiedad Feature. Son distintas versiones del algoritmo que se pueden alternar para conseguir diferentes resultados de la textura procedural. En la figura 6.14, a la izquierda, se puede apreciar el algoritmo f1 y, a la derecha, el algoritmo smooth_f1. El add-on ofrece soporte para varias combinaciones de propiedades, incluyendo 3D con f1, 3D con smooth_f1, y 2D con f1. Además, es compatible con cualquier tipo de métrica de distancia. Para adaptar este nodo a la estructura general de la herramienta, fue necesario ampliar ligeramente la estrategia Voronoi Texture_strategy. Este nodo tiene fun- ciones de precompilación, y llama a varias funciones externas, como por ejemplo funciones de conversión de un vector3 a un color a través de funciones hash. A pesar de ser un mismo nodo, la función HLSL que corresponde a su comporta- miento varía en función de la combinación de sus propiedades, excepto las Distance 3https://en.wikipedia.org/wiki/Worley_noise https://en.wikipedia.org/wiki/Worley_noise 92 Capítulo 6. Implementación del add-on Figura 6.14: Voronoi Features Metric. Es por ello que la herramienta añade una función u otra en función del valor de estas propiedades. 6.1.3.13. Nodo Normal Map Este nodo genera un vector normal a partir de un mapa de normales (figura 6.15). Cuenta con dos entradas diferentes. Por un lado, Color representa el color RGB que codifica el mapa de normales. Por otro lado, Strenght determina la intensidad del efecto del mapeo de la normal. La textura que se conecta al input de color debe estar en modo Non-Color, que indica mapas de textura que no representan colores, sino direcciones de la superficie, para obtener resultados precisos. Figura 6.15: Nodo Normal Map 6.1.4. Transparencia y Corrección Gamma La gestión de la transparencia de los materiales se realiza a través de varios de los comentarios mencionados anteriormente en la plantilla. Tanto el modo Alpha Blend como el modo Alpha Clip requieren incluir la etiqueta que designa el material como transparente en la cola de renderizado: Tags{ "Queue" = "Transparent" } 6.1. Generación del archivo .shader 93 Esta asignación indica a Unity que este objeto se debe renderizar después de dibujar los objetos opacos, puesto que se trata de un objeto con transparencia. Tras especificar su posición en la cola, añadimos una serie de propiedades al pase de renderizado haciendo uso del comentario // Add pass properties en la plantilla. Este comentario nos permite incluir las siguientes propiedades al pase: ZWrite Off Blend [_SrcFactor] [_DstFactor] BlendOp [_BlendOp] La primera de las propiedades desactiva el buffer de profundidad (Z-Buffer, véase sección 2.2.2.1) con el objetivo de que el contenido del buffer de profundidad no se actualice durante el renderizado de este objeto. Las siguientes propiedades definen la operación de mezcla del material transparente, la cual definimos en detalle más adelante en esta sección. Por último, cuando un material tiene transparencia, es necesario desactivar el proceso de culling mediante la opción Cull Off, que se escribe en la plantilla a través del comentario // Add culling. Lo que nos permite el culling es mejorar la eficiencia en el renderizado indicando qué partes de un modelo van a ser, o no, visibles en la imagen final. En el caso de tener materiales con transparencia, es necesario desactivarla para que se vean tanto las caras delanteras del modelo, como las caras traseras, tal y como se aprecia en la figura 6.16a. De esta manera, se evita el problema que se puede observar en la figura 6.16b. (a) Ejemplo de un material transparente con culling desactivado. (b) Ejemplo de un material transparente con culling activado. 6.1.4.1. Alpha Blend Como se describe en la sección 2.2.2.1, el alpha blending consiste en combinar los elementos transparentes, ya sea con el fondo u otros objetos, teniendo en cuenta el canal alfa. 94 Capítulo 6. Implementación del add-on La mezcla es una operación relativamente compleja porque tenemos mucho con- trol sobre cómo se produce exactamente. Generalmente se toman dos valores de color, una fuente y destino, se multiplican los dos por diferentes cantidades y luego se suman. En el shader que estamos generando para Unity, si queremos mezclar el color del fragment con el valor del búfer de color, podemos especificar qué pro- porción de ambos colores se utilizará haciendo uso de la sintaxis: Blend (Ilett, 2022). A la hora de realizar este proceso se deben tener en cuenta los siguientes pará- metros, que podemos encontrar escritos en la sección 6.1.4: _SrcFactor: El parámetro source factor, o factor fuente, se utiliza como mul- tiplicador del color del fragment. Generalmente toma “1” como valor. _DstFactor: El parámetro destination factor, o factor de destino, se utiliza como multiplicador del buffer de color. Generalmente toma el valor resultante de restar uno menos el alpha de la fuente. Existen diferentes maneras de operar con estos parámetros. Aunque por defecto Unity establece la opción Add, que suma origen y destino, se nos permite modificar esta operación a través del parámetro que hemos definido como _BlendOp anterior- mente en la sección de propiedades. En la figura 6.17 se puede observar un ejemplo visual de la suma de dos objetos semi transparentes en la escena de Unity. Figura 6.17: Representación de dos esferas de diferentes colores y con transparencia en Unity utilizando la operación de mezcla “Add” Cambiar este parámetro _BlendOp puede resultar en una gran cantidad de efectos alterando la manera en la que interactúan los parámetros de origen y destino. En la figura 6.18 se puede observar como las mismas figuras de la figura 6.17 se renderizan de distinta forma, al haber cambiado la operación de mezclado. 6.1. Generación del archivo .shader 95 Figura 6.18: Representación de dos esferas de diferentes colores y con transparencia en Unity utilizando la operación de mezcla “Substract” en la esfera roja, y “Add” en la esfera azul Todos estos parámetros pueden ser modificados por el usuario desde las propie- dades del material gracias al uso de los enumeradores que proporciona Unity: [Enum(UnityEngine.Rendering.BlendMode)] _SrcFactor [Enum(UnityEngine.Rendering.BlendMode)] _DstFactor [Enum(UnityEngine.Rendering.BlendOp)] _BlendOp Inicialmente se asignan unos valores automáticamente, de manera que el resul- tado que produce por defecto la herramienta es el que se muestra en la figura 6.17. 6.1.4.2. Alpha Clip Hasta este punto, todos los parámetros que hemos visto relacionados con la transparencia nos han permitido modificar la manera en la que los materiales trans- parentes se ven al mezclarse con su entorno. Sin embargo, el modo Alpha Clip no tiene como objetivo principal la mezcla de elementos, sino la visualización o no visua- lización de ciertas partes o secciones de los objetos en base a un umbral establecido en el material. En la plantilla .shader, establecemos este límite mediante la inclusión de la propiedad _Cutoff haciendo uso del comentario anteriormente mencionado // Add properties. Lo que este parámetro nos permite, en esencia, es crear efectos de recorte o “clip” en la transparencia de un objeto. Los píxeles con un valor de alfa superior a la variable _Cutoff se consideran opacos, mientras que aquellos con un valor de alfa inferior al umbral se consideran transparentes y son recortados, o, lo que es lo mismo, no se escriben en el frame buffer, quedando como un espacio transparente. En la figura 6.19, se pueden observar las diferentes formas en que un mismo material se renderiza sobre un objeto, al cambiar el valor del cutoff o threshold. 96 Capítulo 6. Implementación del add-on Figura 6.19: Visualización del cambio producido en el material al modificar el umbral de Alpha Clip. Fuente: Daniel Ilett 6.1.4.3. Corrección Gamma La necesidad de aplicar corrección gamma en nuestro proyecto surgió al compro- bar que los colores que veíamos en Blender, no se correspondían del todo con los que quedaban representados en Unity. En la figura 6.20 se muestra un ejemplo visual de la representación de colores en Blender, en Unity, y en Unity con la aplicación de la corrección que a continuación se explica. (a) Objeto en Blender, cuyo material tiene valor RGB (0.8, 0, 0.2). (b) Objeto en Unity, cuyo material tiene valor RGB (0.8, 0, 0.2). (c) La misma figura que 6.20b, con corrección gamma sobre el RGB. Figura 6.20: Comparativa de valores RGB en los diferentes espacios de trabajo. El origen de esta corrección radica en los monitores CRT que se utilizaban para pantallas de ordenadores. Estos tenían una propiedad distintiva: la respuesta de color en sus pantallas no era lineal con respecto a los valores sin procesar que se transmitían desde la tarjeta gráfica. No lineal, en este sentido, significaba que los aumentos en uno de los componentes de color en una proporción constante (por ejemplo, si un componente rojo de un color se volvía dos veces mayor) no daba como resultado un aumento de la intensidad de la luz emitida por el monitor por esa misma proporción. Es decir, la luz roja emitida por la pantalla no era el doble de alta. La respuesta de color de un monitor CRT es en realidad una función exponencial. https://danielilett.com/ 6.2. Generación de archivos de imagen 97 Es decir, en la función EmittedLight (C), donde C es un valor de componente de color (rojo , verde o azul) que va de 0 (sin luz) a 1 (intensidad de luz completa), C se eleva a una cierta potencia γ. Este número, γ, se llama exponente gamma o simplemente gamma. Los valores típicos de gamma oscilan entre 2.0 y 2.4, y cuando se trata de gamma en el sentido general, se acuerda que el valor es 2.2 como un compromiso, y muchos de los monitores más nuevos están diseñados para tener el valor gamma de exactamente 2.2. La corrección gamma de una imagen consiste esencialmente en aumentar sus intensidades de color a 1/γ, de modo que cuando el monitor a su vez aumenta el valor a gamma, estos se cancelan y el resultado es el color que originalmente pretendíamos mostrar (Davidović, 2014). De esta manera, cada vez que un color es añadido a la plantilla .shader, es necesario aplicar una corrección gamma de 2.2 al valor proporcionado por Blender. Esto permitirá que los resultados observados en Unity sean visualmente similares a lo que se observa en Blender, evitando incongruencias visuales como la observada en la figura 6.20. Figura 6.21: En un escenario común de gamma = 2.2, así es como el monitor realmente muestra las intensidades de color de tu juego (curva verde). La línea roja punteada muestra cómo un monitor lineal mostraría las mismas intensidades. Fuente: David Davidović En nuestro proyecto hemos aplicado dicha corrección para cada variable de tipo “Color” detectada en Blender al escribirlos como propiedades en la plantilla de Unity, o al trabajar con nodos que rutilizan este tipo de variable como Color Ramp, Mix o RGB. 6.2. Generación de archivos de imagen Durante el proceso de escritura de los nodos en la plantilla, descrito en la sección 6.1.3, es posible que se hayan encontrado nodos como Image Texture que contengan https://gamedevelopment.tutsplus.com/es/correccion-gamma-y-por-que-es-importante--gamedev-14466a 98 Capítulo 6. Implementación del add-on una referencia a un archivo de imagen. Además de hacer las modificaciones pertinen- tes en la plantilla del .shader para mostrar la imagen sobre el material resultante, es importante que la estrategia que maneje estos nodos guarde, de alguna manera, una referencia a la imagen para poder generar una copia de la misma más adelante. Esto se hace guardando la ruta de la imagen utilizada en Blender en una variable de código imagesMap. Una vez terminado el proceso de iteración sobre los nodos, se recorren los valores almacenados en dicha variable y se genera una copia de cada uno de los archivos de imagen utilizados en un directorio textures, ubicado en la ruta de exportación seleccionada por el usuario. Es necesario generar una copia de los archivos de imagen pues estos deben te- ner su propio archivo .meta correspondiente para que puedan ser asociados a los materiales generados. Para que el archivo de imagen funcione correctamente en la escena, debe encontrarse en el proyecto de Unity, motivo por el cual es necesario realizar la copia del fichero, y no basta con que se encuentre en cualquier ruta del dispositivo. Por lo tanto, la copia de cada archivo de imagen utilizado se genera en un directorio llamado textures, ubicado en la ruta de exportación seleccionada por el usuario. Junto con cada imagen copiada, se genera su respectivo archivo .meta. De esta manera, se asegura que cada imagen tenga su archivo .meta correspondiente, permitiendo la vinculación de la imagen a las propiedades del material, tal y como se explicará en la siguiente sección. En la figura 6.22 se puede observar el contenido de un directorio donde se ha exportado un material con una textura asociada, y el contenido del directorio donde se encuentra la textura, junto con los archivos .meta necesarios, generados tal y como se ha descrito en esta sección. (a) Ficheros resultantes del proceso de exportación de un material ImageMaterial. (b) Contenido del directorio textures de la figura 6.22a. Figura 6.22: Archivos generados por la herramienta para un caso de ejemplo. 6.3. Generación del archivo .mat Como se puede leer en el capítulo 4, para crear materiales en Unity es necesario combinar varios elementos esenciales: el shader (.shader), el material (.mat) y las 6.3. Generación del archivo .mat 99 texturas asociadas, si las hubiera. Estos componentes trabajan en conjunto para definir cómo se ve un objeto en la escena. Para que eso suceda, es necesario asociar los archivos entre ellos mediante el uso de su identificador único global. Por ese motivo es necesario que la herramienta desarrollada genere, además de los archivos pertinentes, sus archivos .meta donde se define un identificador único para cada archivo generado. Para generar estos archivos de manera personalizada, se utilizan de nuevo plantillas que se sobrescriben para añadir las propiedades e identificadores necesarios. Para automatizar la creación de los archivos .meta se ha creado un archivo llamado meta_generator.py, el cual contiene distintos métodos para generar un identificador propio para el .meta y sobrescribir una plantilla extensión archivo.meta. Recapitulando los archivos generados hasta este punto, contamos con el .shader y los archivos de imagen. Junto con estos archivos se habrá generado su archivo .meta correspondiente, proporcionando un identificador global único a cada archivo, con la ayuda del archivo meta_generator.py mencionado anteriormente. Mientras se generaron estos ficheros, los identificadores únicos se han ido guardando en variables, de modo que son accesibles en el siguiente paso de la exportación. Para generar el archivo de material, .mat, se parte de la siguiente plantilla: %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: ${material_name}$ m_Shader: {fileID: 4800000, guid: ${shader_guid}$, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 m_ValidKeywords: [] m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: {% if not tex_env_strings %}[]{% else %} {% for tex_env_string in tex_env_strings -%} -${tex_env_string}${% endfor -%}{% endif -%} 100 Capítulo 6. Implementación del add-on m_Ints: [] m_Floats: [] m_Colors: {% for variable in colors_strings -%} -${variable}${% endfor %} m_BuildTextureStacks: [] Las secciones de la plantilla anterior que se encuentran entre los símbolos $...$ son las secciones que deberán ser sustituidas por la herramienta durante el proceso de exportación. Estas secciones son: material_name: Como su nombre indica, esta variable debe ser sustituida por el nombre del material. Como se explicó al inicio de este capítulo, el nombre del material coincide con el del material en Blender. shader_guid: Debe ser sustituido por el identificador único del archivo .sha- der generado en los pasos anteriores. Este habrá sido guardado en una variable y se asignará directamente a esta sección de la plantilla. tex_env_string: En esta sección se definen las texturas asociadas al ma- terial. Utiliza bloques de código condicionales para manejar casos en los que el material no esté conformado por ninguna textura o imagen. A la hora de rellenar la plantilla es posible que la variable tex_env_string no contenga ninguna textura, en cuyo caso se dejará esta sección vacía. En caso contrario, se escribirá cada textura en el formato adecuado asignando el identificador único global correspondiente a su archivo de imagen. A continuación se mues- tra el formato que siguen las texturas al ser añadidas a esta sección de la plantilla, donde texture_name indica el nombre de la propiedad del shader que representa la textura, y texture_guid corresponde al identificador único del archivo de imagen. m_TexEnvs: - {texture_name}: m_Texture: {fileID: 2800000, guid: {texture\_guid}, type: 3} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} variable: Este comentario permite añadir a la plantilla variables necesarias para el shader. Es útil para añadir información que no haya sido añadida previamente en forma de propiedad al realizar el recorrido de los nodos de material. En el estado actual de la herramienta, son añadidos los valores má- ximo y mínimo de la bounding box del objeto, necesarios para la representación de las coordenadas Generated del el nodo Texture Coordinate, tal y como se explica en la sección 6.1.3.7. 6.4. Generación del archivo .fbx 101 6.4. Generación del archivo .fbx Para poder generar un prefab de Unity que represente un objeto en la escena, es esencial asociar la geometría del objeto además de su material. Para ello, es necesario exportar junto con todos los demás archivos aquel que contenga la geometría del objeto exportado. Esto se consigue utilizando la API de Blender, pues proporciona funciones que permiten exportar el archivo FBX del objeto de la escena de Blender seleccionado. De nuevo, además del archivo de geometría, también se generará su archivo .meta para contener su identificador único y permitir la correcta asociación del archivo al prefab en los siguientes pasos de la exportación. Para exportar el archivo FBX correctamente, es necesario ajustar el objeto en Blender para que la coordenada Y represente la dirección vertical en Unity. Además, Blender utiliza metros como unidad de medida para los modelos, mientras que FBX utiliza centímetros. Por lo tanto, antes de exportar el archivo FBX, se debe realizar una conversión de unidades. Esto se logra mediante la función Apply Transform en Blender, que convierte las medidas de metros a centímetros. 6.5. Generación del archivo .prefab Llegados a este punto se habrán generado todos los archivos necesarios para representar adecuadamente un material en Unity, a falta únicamente de un prefab que funcione como contenedor del material y de la geometría para poder visuali- zar los materiales generados con mayor facilidad. La generación de dicho prefab se realiza de nuevo utilizando la técnica de rellenar plantillas con marcadores situados en aquellos lugares donde debe colocarse la información necesaria para el correcto funcionamiento del archivo. De este modo, la herramienta asocia los identificadores únicos generado para el material y para la geometría (.fbx) al archivo .prefab, asig- nándose estos como el material y la geometría que se mostrarán sobre el objeto que representa en la escena de Unity. Una característica que encontramos al completar la plantilla del prefab con los datos de la geometría es el campo FileID, necesario para identificar la malla que se quiere representar. Este campo es generado a partir de la aplicación del algoritmo de cifrado xxhash sobre una cadena de texto que contiene el nombre de la malla. Para poder realizar esta operación se ha programado una versión del algoritmo xxhash en Python, siguiendo la implementación explicada en el repositorio de GitHub corres- pondiente a la implementación oficial de este algoritmo de hash para Python4. El motivo por el que no se ha utilizado la librería xxhash directamente es para evitar añadir dependencias al add-on, añadiendo manualmente únicamente la función que se va a utilizar en el proceso de exportación. Al asociar la geometría del objeto con su material correspondiente en un prefab, 4https://github.com/Cyan4973/xxHash https://github.com/Cyan4973/xxHash 102 Capítulo 6. Implementación del add-on finaliza el proceso de exportación de Blender a Unity, solventando el problema que se planteaba al inicio del proyecto. Esta automatización de la generación del prefab con todos los elementos necesarios elimina la necesidad de realizar las tediosas ta- reas manuales, ahorrando tiempo a los desarrolladores e incluso reduciendo posibles errores humanos. En la figura 6.23 se puede observar un caso de ejemplo del proceso de exportación, mostrando el resultado obtenido en Unity junto con los archivos generados vistos desde el editor. Figura 6.23: Objeto exportado a Unity, junto con sus archivos en la vista de editor. 6.6. Empaquetado del Add-on Finalmente, se explica cómo se ha generado el paquete del add-on. Para la crea- ción de la interfaz del add-on se utiliza una librería Blender Development, que sirve para la creación de add-ons en Blender. El archivo __init__.py contiene la decla- ración de la interfaz del add-on y sus funcionalidades. Normalmente, la creación del add-on para su instalación se realizaría compri- miendo el código fuente junto con su archivo __init__.py en un archivo .zip. Sin embargo, debido al uso de librerías externas como jinja2, se han creado dos archivos adicionales: setup.cfg: proporciona configuraciones adicionales y específicas para la cons- trucción y distribución del paquete, así como las dependencias adicionales necesarias para el add-on. setup.py: configura el paquete Python para la distribución e instalación, ges- tionando sus metadatos, dependencias y la estructura del paquete. 6.7. Resultados 103 6.7. Resultados Como apéndice (A) se podrá encontrar la guía paso a paso que se incluye en el repositorio sobre cómo utilizar el add-on en Blender, indicando cómo se instala y cómo debe ser su uso para generar los archivos correctamente. Para ilustrar el funcionamiento de la herramienta, se muestran a continuación una serie de casos de ejemplo. En las figuras 6.24, 6.25, 6.26, 6.27, 6.28, 6.29 y 6.30 se muestra un modelo conformado por uno o varios materiales creados en Blender, junto a su resultado en Unity al ser exportado por el add-on. La figura 6.24 representa un modelo con un único material asociado. Se puede observar como se ha exportado correctamente y se mantiene el aspecto visual en el entorno de Unity. Figura 6.24: Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. Figura 6.25: Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. La figura 6.25 es un modelo complejo con una larga lista de materiales diferentes asociados. Sin embargo, al exportar el modelo con la herramienta y visualizarlo 104 Capítulo 6. Implementación del add-on en Unity, podemos observar como se han mantenido asociados correctamente. La diferencia en la iluminación y las sombras en esta figura se debe a la colocación de las luces en las escenas de Blender y de Unity. En la figura 6.26 de nuevo se observan diferencias en la iluminación que recibe el modelo. Estas diferencias nos ayudan a comprobar que, a pesar de haber cambiado el modelo de iluminación y las luces presentes en la escena, los materiales se mantienen y se renderizan adecuadamente en este nuevo contexto. Figura 6.26: Modelo exportado con la herramienta. A la izquierda, como se ve en Blender. A la derecha, como se ve en Unity. (a) Modelo en la escena de Blender. Fuente: Sketchfab (b) Modelo exportado, en la escena de Unity. Figura 6.27: Modelo exportado con la herramienta. El complejo modelo de la figura 6.28 permite apreciar la utilidad de la herra- mienta desarrollada. Siguiendo el proceso de exportación actual, habría que realizar el baking de las texturas para cada elemento del modelo que utilice un material, a pesar de que se trate del mismo material. Es decir, habría que realizar el proceso para el tejado de la casa, para sus columnas, para el puente y cualquier elemento https://sketchfab.com/3d-models/one-piece-mera-mera-no-mi-ac8c016683fb4712ac4c8f6781f4babf 6.7. Resultados 105 que use el material de color rojo. Sin embargo, con el uso de la herramienta, se ha exportado con un solo click. (a) Modelo en la escena de Blender. Fuente: Free 3D (b) Modelo exportado, en la escena de Unity. Figura 6.28: Modelo exportado con la herramienta. Conclusiones La herramienta desarrollada para este proyecto, en forma de add-on de Blender, genera los archivos necesarios para representar un objeto en Unity con un material asociado al mismo. Soporta un total de catorce nodos de Blender diferentes, propor- cionando un resultado visual coherente entre ambas plataformas. Además, también proporciona soporte para aplicar características como transparencia o clipping a los materiales. En el siguiente capítulo, se exponen las pruebas con usuarios realizadas para poner a prueba el funcionamiento de la herramienta, así como para conocer las opiniones de los usuarios sobre su uso. https://free3d.com/es/modelo-3d/house-in-the-beach-659371.html 106 Capítulo 6. Implementación del add-on (a) Modelo en la escena de Blender. Fuente: Free 3D (b) Modelo exportado, en la escena de Unity. Figura 6.29: Modelo exportado con la herramienta. (a) Modelo en la escena de Blender. (b) Modelo en la escena de Unity. Figura 6.30: Modelo exportado con la herramienta. https://free3d.com/es/modelo-3d/voodoo-doll-130819.html Capı́tulo 7 Pruebas de evaluación con usuarios Durante la realización del proyecto se emplearon las pruebas y encuestas a usua- rios como recurso. Fueron de utilidad tanto para conocer opiniones acerca del uso de Blender, como para observar cómo se desenvuelven los usuarios utilizando el add-on, para así detectar y corregir posibles errores o fallos técnicos (bugs) en el mismo. 7.1. Objetivo de las pruebas Objetivo 1 Comprobar la correcta instalación del add-on en diferentes dispositivos en la version 3.6 de Blender. • ¿Surge algún error a la hora de instalar el add-on en la versión 3.6 de Blender? Objetivo 2 Comprobar el correcto funcionamiento del add-on cuando es utilizado con libertad por el usuario • ¿Surge algún error en Blender a la hora de generar el material? • ¿El material se genera en la carpeta seleccionada por el usuario? Objetivo 3 Comprobar el correcto funcionamiento de los shaders exportados a Unity 107 108 Capítulo 7. Pruebas de evaluación con usuarios • ¿Los shaders compilan en Unity? • ¿El material que se ha generado tiene las mismas propiedades en el ins- pector que en Blender? Objetivo 4 Comprobar que el resultado en Unity se asemeja estéticamente al material creado en Blender por el usuario 7.2. Requisitos para los participantes Usuarios objetivo Aficionados de Blender interesados en la creación de contenido 3D. Estudiantes de Desarrollo de Videojuegos que utilicen Blender para crear As- sets para sus videojuegos en Unity. Profesionales de la industria de videojuegos y animación. Número de participantes: 7. Entorno de Pruebas: Utilización de ordenadores personales de los usuarios. 7.3. Métodos de prueba Para garantizar la eficacia y la usabilidad de nuestro proyecto, diseñamos un proceso de pruebas basado en métodos de observación directa y la recopilación de datos a través de un cuestionario. Durante las sesiones de prueba, los participantes tuvieron la oportunidad de in- teractuar con el proyecto en un entorno controlado. Estuvimos presentes para obser- var sus acciones,y resolver posibles dudas, lo que nos permitió obtener información valiosa sobre cómo los usuarios interactuaban con la herramienta. Además de la observación directa, utilizamos un cuestionario diseñado específi- camente para recopilar información sobre la satisfacción del usuario, la facilidad de uso, las áreas de mejora y cualquier otra retroalimentación relevante. Este cuestio- nario nos ayudó a obtener datos cuantitativos y cualitativos que complementaron nuestras observaciones y nos proporcionaron una comprensión más completa del uso de la herramienta. 7.4. Pruebas realizadas 109 Al finalizar la prueba se les pidió a los usuarios realizar una encuesta en un formulario de Google1. En el siguiente documento se puede observar el proceso de pruebas que siguieron los usuarios: Prueba con Usuarios. 7.4. Pruebas realizadas Las pruebas se han realizado con siete usuarios diferentes. A continuación se exponen las respuestas y estadísticas obtenidas a partir de las mismas. Los usuarios, tal y como se muestra en la figura 7.1, poseen diversos niveles de conocimiento sobre Blender. Figura 7.1: Valoración de los usuarios de su conocimiento de Blender. Los errores que se presentan en la estadística (figura 7.2) no han sido fallos cruciales de compilación de shaders, sino detalles menores, como que una textura procedural no se asemeje por completo a lo que se ve en Blender, fallos con las normales o errores en el prefab. 1https://forms.gle/VfcNaMZ7joy1Sdb67 https://docs.google.com/document/d/1rQbYyJ-0iulJiT2xHDQZh3oWsli31yQBX3owgBFTeXw/edit?usp=sharing https://forms.gle/VfcNaMZ7joy1Sdb67 110 Capítulo 7. Pruebas de evaluación con usuarios Figura 7.2 Finalmente, el 71.4 % considera que usaría la herramienta en el futuro cuando esté más completa, frente al 28.6 % que la usaría en su estado actual (figura 7.3). Ningún usuario ha mostrado intención de no utilizar la herramienta, lo que nos confirma su utilidad. Figura 7.3 Respondiendo a las preguntas de investigación que se planteaban anteriormente, podemos concluir lo siguiente para cada objetivo: 1. No surge ningún error a la hora de instalar el add-on en Blender. 2. Los elementos se generan en el directorio seleccionado por el usuario, y no surge ningún error en Blender. 3. Todos los shaders generados para Unity compilan, y las propiedades corres- ponden a las de Blender. 7.4. Pruebas realizadas 111 4. Se han observado ligeras discrepancias en el aspecto visual en algunas de las pruebas realizadas. En conclusión, las pruebas nos han ayudado a detectar errores tales como un error en la función HLSL del nodo Normal Map, y la asociación de los materiales a los prefabs cuando el objeto cuenta con varios materiales asociados sobre la misma malla, así como las diferencias que se observan en las texturas procedurales en al- gunos de los modos de configuración. Esto nos ha permitido solucionar los errores y obtener una herramienta más robusta. En las figuras 7.4, 7.5, 7.6 y 7.7 se muestran algunos ejemplos de las pruebas realizadas. Las figuras de la columna izquierda corresponden el modelo visto en la escena Blender y las de la derecha en la escena de Unity. Se puede observar la co- rrespondencia visual entre ambos modelos, garantizando el funcionamiento correcto de la herramienta desarrollada. Figura 7.4: Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. Figura 7.5: Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. 112 Capítulo 7. Pruebas de evaluación con usuarios Figura 7.6: Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. Figura 7.7: Material que el usuario ha creado en el proceso de pruebas de usuario, y el resultado en Unity. Capı́tulo 8 Conclusiones y Trabajo Futuro El objetivo de este capítulo es exponer las conclusiones derivadas del proceso de desarrollo de este proyecto, además de establecer posibles objetivos futuros que permitieran ampliar la herramienta obtenida. 8.1. Conclusiones El proyecto desarrollado ha resultado en un add-on para facilitar el proceso de exportación de materiales y modelos 3D desde Blender a Unity. La herramienta reduce significativamente el tiempo del proceso de exportación típico que llevan a cabo los desarrolladores actualmente. Además, permite mantener la flexibilidad de los materiales en Unity, mediante los parámetros de los materiales que modifican el comportamiento del shader. Esto puede suponer un gran apoyo para los artistas, eliminando la necesidad de realizar el proceso de baking, y permitiendo modificar sus materiales fácilmente en el entorno de Unity. El desarrollo de este add-on requirió un estudio exhaustivo de conceptos de in- formática gráfica. Se estudió también el funcionamiento del pipeline de renderizado tanto en Unity como en Blender, poniendo un especial énfasis en los motores de renderizado y pipelines de renderizado diferentes que estos ofrecen, y sus diferen- tes usos. Este estudio conllevó a concluir que se utilizaría el motor de renderizado Eevee en Blender, y la Universal Render Pipeline en Unity. Esto ha ayudado a com- prender en profundidad el proceso del renderizado, el funcionamiento de los shaders y de los materiales en entornos 3D, así como los motivos tras los problemas que habitualmente surgen en la creación y exportación de materiales. Fue también especialmente relevante la investigación sobre la programación de shaders en Unity, utilizando HLSL. Gracias a esto se posibilitó la traducción de los materiales de Blender a archivos .shader de Unity. Para conseguir esto, también fue necesaria la investigación sobre el código fuente de Blender, concretamente la forma 113 114 Capítulo 8. Conclusiones y Trabajo Futuro en que maneja los nodos de material y cómo los representa. El add-on implementa soporte para catorce nodos diferentes de Blender, permi- tiendo la exportación de archivos .shader, .material, .fbx y .prefab, junto con sus respectivos archivos .meta. Su diseño facilita la incorporación de nodos adicionales de manera sencilla para los desarrolladores que deseen ampliar la herramienta. Con todo esto, podemos concluir que los resultados obtenidos han sido satisfac- torios, cumpliendo con los objetivos planteados en un primer momento y propor- cionando una solución sólida al problema de la falta de flexibilidad a la hora de exportar materiales de Blender a Unity. Finalmente, para evaluar la eficacia y correcto funcionamiento de la herramienta, se realizaron pruebas con usuarios. Estas pruebas, a pesar de sacar a la luz pequeñas imprecisiones de la herramienta, permitieron corroborar el buen funcionamiento del add-on y la satisfacción con el mismo de los potenciales usuarios. 8.2. Trabajo Futuro A pesar de que la herramienta desarrollada cumple con las expectativas que teníamos de la misma, existen diversas ampliaciones que se podrían incorporar para que su integración en el flujo de trabajo habitual en Blender sea óptima. Dichas ampliaciones se presentan a continuación como posible trabajo futuro. En primer lugar, sería óptimo ampliar los nodos ya existentes en la herramien- ta. Algunos de los nodos implementados presentan varias configuraciones que permiten obtener nuevos resultados visuales, como pueden ser diferentes fun- ciones para el nodo Voronoi Texture, o las diferentes funciones de mezclado del nodo Mix. Como trabajo futuro, podrían estudiarse estas diferentes configura- ciones y añadirse al add-on. Gracias a la estructura modular con la que se ha desarrollado la herramienta, esta ampliación supondría una leve modificación del código existente, siendo la principal ampliación la integración de dichas funciones en el lenguaje HLSL. Tal y como indicaban los usuarios durante el desarrollo de las pruebas, el add- on mejoraría significativamente si ofreciera soporte para procesar más tipos de nodos de material. Destacan aquellos que fueron mencionados en las encuestas pero no fueron implementados, como el nodo Noise Texture o el nodo Bump. De nuevo, gracias al desarrollo modular utilizado en el desarrollo del add-on, integrar estos nodos en el proceso de exportación sería posible sin tener que modificar la arquitectura de código subyacente. El trabajo futuro consistiría en estudiar estos nuevos tipos de nodos, e interpretarlos correctamente en un shader de Unity. Conllevaría la creación del struct y la función HLSL correspondiente para cada nuevo tipo de nodo. 8.2. Trabajo Futuro 115 Otra posibilidad planteada para trabajo futuro, sería dotar a la herramienta la capacidad de exportar la escena de Blender al completo. De esta manera, se exportarían todos los objetos de la escena, en lugar de únicamente el se- leccionado por el usuario. Además, esta ampliación incluiría la capacidad de exportar las luces y la cámara o cámaras de la escena de Blender, a Unity. Es- te proceso no sería trivial, pues conllevaría el estudio en profundidad de cómo se interpretan y representan estos elementos en ambas plataformas, así como la manera de traducirlos adecuadamente de Blender a Unity, manteniendo la correspondencia visual y funcional lo más similar posible. En cualquier caso, esta ampliación sería una opción adicional para el usuario a la hora de realizar la exportación, permitiéndole en cualquier caso poder ejecutar el proceso de la forma actual, únicamente del objeto seleccionado. Por último, sería interesante trabajar las capas Volume y Displacement de los materiales de Blender. Esto no sólo permitiría representar una gran variedad de materiales en Unity, sino que además eliminaría la restricción de no po- der utilizar estas capas en los materiales que exporta la herramienta, la cual puede ser muy restrictiva para algunos usuarios de Blender. Esta ampliación conllevaría un profundo estudio del funcionamiento de estas capas y cómo in- terpretarlas correctamente en un shader de Unity, así como ampliar el add-on para que integre esta nueva funcionalidad en el código ya existente. Capı́tulo 9 Contribuciones Personales Miriam Martín Sánchez Al comienzo del proyecto, realicé una investigación sobre la programación en HLSL y ShaderLab, componentes fundamentales de nuestro trabajo. Redacté docu- mentos sobre el funcionamiento de las propiedades de ShaderLab y elaboré una guía para programar en HLSL. Durante esta investigación, profundicé en conceptos clave como el Vertex Shader, el Fragment Shader y la conversión de espacios de coorde- nadas. Además, investigué varios motores de renderizado en Unity, incluyendo el Universal Render Pipeline (URP), el High Definition Render Pipeline (HDRP) y el Built-In Render Pipeline. Al comienzo del proyecto hice una aproximación inicial muy sencilla sobre el dfs para el recorrido de nodos , su inserción de propiedades en la plantilla del shader, métodos y variables. Pero esto fue un prototipo, que más tarde Elisa lo extendió mejorándolo completamente. Me he encargado de la creación de la interfaz del add-on . Junto con mis compañeras, colaboré en la creación del archivo Strategy, una parte fundamental de la arquitectura del add-on para el manejo de nodos. Además, contribuí a la refactorización de los nodos para tuvieran su propia estrategia.También colaboré con mis compañeras en la refactorización del código para el uso de structs en los métodos de HLSL. Una parte fundamental de mi trabajo se centró en la investigación y traducción de varios nodos al lenguaje HLSL. Cada nodo presentaba desafíos que requirieron un enfoque meticuloso y un entendimiento de su funcionamiento. El nodo más com- plejo que implementé fue el nodo Voronoi Texture. Este nodo implicó modificar la plantilla para la inclusión de includes y defines, además de la creación de métodos auxiliares. Esta tarea fue especialmente desafiante debido a la complejidad del algo- ritmo Voronoi y la necesidad de integrarlo adecuadamente en la estructura existente 117 118 Capítulo 9. Contribuciones Personales del add-on. Otro nodo que requirió una investigación exhaustiva fue el nodo Texture Coordinate. Tuve que investigar cómo traducir las coordenadas de textura de Blen- der a Unity, centrándome en particular en la coordenada “Generated” y el problema con la bounding box. Esta tarea implicó comprender en profundidad la manera en que Blender y Unity interpretan las coordenadas de textura y encontrar la corres- pondencia adecuada entre ellas. Además, amplié el nodo Principled BSDF para que admitiera normales y emisión, y proporcionara soporte para que este shader obtu- viera luces de la escena y generara sombras. El nodo Color Ramp presentó un desafío adicional, ya que Blender no mostraba de manera clara su implementación. Tuve que realizar una implementación individual investigando cómo mezclar colores dado un factor y las posiciones de los colores en el rampa de color. Asimismo, desarrollé el nodo Image Texture, que implicó acceder a la ruta de la imagen para ser guardada en un diccionario y posteriormente exportada junto con las texturas. También, im- plementé el nodo Checker Texture y el nodo Shader to RGB. Finalmente implemente el nodo Normal Map con mi compañera Elisa. Otro desafió que enfrentamos es la conversión de tipos ya que en Blender es posi- ble conectar entre los nodos un float a un float4 y todas las posibles combinaciones. Por lo que cree la carpeta Utils con todas las combinaciones de conversiones de ti- pos que se pueden hacer en Blender para que luego en HLSL no haya problemas de compilación o que los resultados no queden igual que en Blender. Otra parte de mi contribución se centró en la generación de texturas y materiales para el add-on: En primer lugar, me encargué de copiar las imágenes desde las rutas especifica- das y crear sus correspondientes archivos .meta. Estos archivos .meta incluían un identificador único necesario para el enlace de las propiedades del material, asegu- rando así una correcta asociación entre las texturas y los materiales en el proceso de importación a Unity. Además, desarrollé la lógica para generar el archivo .material, el cual incluía la asignación adecuada de las texturas y otras propiedades relevantes. Por otro lado, me ocupé de la generación del archivo .fbx, lo cual implicó una investigación sobre la correcta configuración de la exportación para garantizar que los materiales se visualizaran correctamente en Unity. Esto incluyó la creación de una plantilla .meta específica para el archivo .fbx. Además, me encargué de la gestión de dependencias y la creación del add-on para su integración en Blender. Esta tarea implicó investigar cómo manejar estas depen- dencias externas y resolver cualquier problema que surgiera en el proceso. Se creó los archivos setup.py y setup.config, los cuales fueron diseñados específicamente para abordar el desafío de la creación del add-on. Estos archivos proporcionaron una estructura y configuración adecuadas para asegurar que todas las dependencias necesarias fueran gestionadas correctamente. Junto a mis compañeras hemos dedicado juntas a redactar esta memoria para plasmar todos nuestros conocimientos obtenidos y nuestro trabajo. 119 Paula Morillas Alonso Al comenzar el proyecto dediqué un tiempo a realizar pruebas que demostraran el fundamento de nuestro propósito, comparando así el aspecto de una escena con diferentes materiales aplicados a modelos en Blender y su representación correspon- diente en Unity. Estas pruebas permitieron validar nuestra propuesta de trabajo y abrieron camino a una fase de investigación por parte de todas las autoras, en diferentes direcciones. Mi investigación pasó a centrarse en el funcionamiento de la iluminación en ambos entornos así como sus similitudes y diferencias. Partiendo de esta base, he tenido la oportunidad de indagar y poder plasmar sobre esta memoria múltiples conceptos relacionados con técnicas de iluminación, tipos de luces, métodos de sombreado o los diversos modos de renderizado y sus limitaciones en cuanto a la representación de la iluminación. Esta exploración permitió desarrollar una parte fundamental de mi trabajo: La implementación del nodo Principled BSDF. Esta fue la tarea más compleja y du- radera a la que me enfrenté durante el proyecto dado que, para su desarrollo, fue necesario probar múltiples técnicas con las que se pudiera replicar el aspecto de los shaders de tipo Lit, que Unity proporciona por defecto, sin tener acceso al códi- go de estos. Una mayor investigación en los recursos proporcionados por el propio paquete de Universal Render Pipeline, me permitió simplificar el desarrollo de esta tarea al poder acceder a funciones relacionadas con la iluminación, que se adaptaron fácilmente a la estructura del nodo de Blender. Una vez implementado el nodo Principled BSDF pasé a centrarme en gestionar algunas de sus propiedades en mayor profundidad. En concreto comencé a trabajar en desarrollar código que permitiera replicar en Unity el efecto de transparencia en modo Alpha Blending que puede tener un material de Blender. Esta tarea supuso una nueva investigación, esta vez centrada en conocer a fondo los conceptos clave de la transparencia en la informática gráfica, los ajustes de los materiales de Blender en sí y los parámetros que se debían modificar en Unity para tratar con esta propiedad. Esta nueva búsqueda abrió la puerta a modificar el código para gestionar pará- metros fundamentales para la correcta representación de materiales transparentes en Unity como son las colas de renderizado o la desactivación del buffer de pro- fundidad. En base a estas modificaciones pasé a gestionar otras propiedades más avanzadas como la activación o desactivación del backface culling en Blender y su correspondiente representación en Unity. Con esta tarea finalizada, pasé a adaptar otro de los modos de transparencia en Blender, el modo Alpha Clip, para su funcionamiento en Unity. Esta parte supuso el desarrollo del código necesario para gestionar tanto el funcionamiento del modo en sí, como la modificación de uno de sus parámetros clave (Cuttoff ) desde la pestaña de propiedades del material. 120 Capítulo 9. Contribuciones Personales Tras implementar los dos modos de transparencia más utilizados en Blender, am- plié mi investigación en este tema pasando a centrarme en gestionar los diferentes modos de mezcla y operaciones de mezclado que podían tener los objetos transpa- rentes. Dado que Blender no permite cambiar estas opciones y establece valores por defecto, esta parte del desarrollo se centró tan solo en Unity. En lo referente a la arquitectura del proyecto, colaboré con mis compañeras en la integración del patrón Strategy, una parte fundamental de la arquitectura del add-on para el manejo de nodos, contribuí a la refactorización de los nodos para tuvieran su propia estrategia y colaboré en la refactorización del código para el uso de structs en los métodos de HLSL. Por otra parte, en cuanto a tareas ligadas al desarrollo del add-on en sí he trabajado en su modificación para permitir al usuario seleccionar la ruta de guardado de los archivos. Por último, he colaborado con mis compañeras en la redacción de esta memo- ria que expone toda nuestra investigación, así como el desarrollo completo de la herramienta y los resultados que se han obtenido de realizar pruebas con esta. Elisa Todd Rodríguez Al comienzo del proyecto gran parte de mi trabajo consistió en investigar sobre diferentes aspectos relevantes para el planteamiento del proyecto, redactando docu- mentos de apoyo para compartir las investigaciones con el resto de integrantes del equipo. Comencé por realizar un estudio exhaustivo de la tecnología que hay tras los motores de renderizado de Blender, Eevee y Cycles. Redacté en un documento la información oficial que Blender proporciona sobre estos motores poniendo especial énfasis en el uso que se le da a cada uno desde el punto de vista del usuario. Esto sir- vió como base para tomar la decisión de utilizar Eevee como el motor de renderizado que se debe utilizar en Blender si se pretende hacer uso de nuestra herramienta. La siguiente investigación que llevé a cabo fue el análisis en profundidad de los shaders en Blender. Concretamente, me centré en el nodo Principled BSDF y a partir de ahí investigué el funcionamiento del mismo en Blender. Añadí información impor- tante sobre el funcionamiento de los shaders GLSL, y describí el comportamiento del shader GLSL que define Blender para el nodo mencionado. Esto ayudó a obte- ner unas nociones sobre cómo Blender trabaja los materiales y nodos de material a bajo nivel. Como última investigación, estudié y comprendí la estructura interna de los archivos de material en Unity (.mat), haciendo énfasis qué características sería necesario tener en cuenta para desarrollar nuestros propios materiales a través de la herramienta. Una vez completamos la investigación inicial y pusimos en común la información obtenida, comenzamos a implementar la herramienta en Python. Mi principal tarea durante el desarrollo de la herramienta fue programar la iteración sobre los nodos de 121 material y diseñar un sistema que a base del uso del patrón de diseño Strategy per- mitiera una fácil ampliación del código para soportar más nodos. Para la iteración sobre los nodos, desarrollé una función que recorre cada nodo dentro de una estruc- tura de materiales, procesando sus propiedades y conexiones. Creé una interfaz base llamada Strategy que declara las operaciones comunes necesarias para procesar los nodos. Para asegurar el correcto funcionamiento de este sistema, implementé las es- trategias para los nodos Value y RGB. Además, desarrollé una clase de contexto que permite que, para añadir soporte a un nuevo tipo de nodo, solo sea necesario crear una nueva estrategia concreta que implemente la lógica específica para ese nodo. Para permitir que cada estrategia escriba en la plantilla del shader adecuadamen- te, cree funciones que interactúan directamente con la plantilla y permiten añadir el código específico a diferentes zonas como las propiedades o las funciones. El otro gran bloque de código que programé fue la utilización de structs en el .shader para almacenar la información que generan las estrategias. Esto conllevó añadir funciones que permiten añadir dichos structs a la plantilla desde Python, así como establecer cómo cada estrategia debe añadir su struct al shader. En cuanto a los nodos implementados, además de los mencionados anteriormente, me ocupé de integrar los nodos AddShader, MixShader y Mix. Este último conllevó mayor carga de trabajo para poder implementarlo en todas sus configuraciones. Para este nodo también añadí código para permitir la inclusión dinámica de una función de mezclado u otra en función de las propiedades del nodo. Esto conllevó la investigación del código de Blender para conocer y replicar en HLSL las funciones de blending o de mezclado específicas para los diferentes modos de mezcla como Mix y Add. Por último, implementé junto con Miriam Martín el nodo Normal Map. A todo esto se suman tareas de programación como pueden ser limpiezas de código, refactorizaciones y revisiones cada cierto tiempo para asegurar el correcto funcionamiento de la herramienta. Cabe mencionar la localización el problema de la corrección gamma al exportar los materiales de Blender a Unity, de forma que añadí funciones y código para adaptar los colores al espacio de Unity cada vez que se escriben en la plantilla. En los últimos pasos del desarrollo de la herramienta, realicé el proceso de gene- ración del archivo de prefab de Unity. Esto conllevó hacer un primer análisis de un archivo de prefab con la estructura que necesitábamos, es decir, con los componentes mínimos necesarios para representar un objeto en la escena. Al hacer este estudio, descubrí una peculiaridad que existe a la hora de asignar una geometría o malla a un componente, que me llevó a investigar el origen de un identificador FileID. Puesto que Unity no informa explícitamente en su documentación cómo generan ese núme- ro, fue necesario investigar foros de discusión y realizar pruebas manualmente hasta dar con la solución. Une vez averigüé que el identificador se obtenía aplicando el al- goritmo de cifrado xxhash sobre una cadena concreta, debía integrar este algoritmo de cifrado dentro del código del add-on. Esto conllevó un estudio de cómo se realiza este algoritmo paso a paso para a continuación recrearlo a mano en el entorno de 122 Capítulo 9. Contribuciones Personales Python. Una vez hice esto, creé la plantilla para el archivo de extensión .prefab y adapté el código de la herramienta para rellenar la plantilla con los identificadores necesarios. Al igual que mis compañeras, también dediqué tiempo de trabajo a plasmar en este documento el trabajo realizado, redactando diferentes secciones que varían desde explicaciones iniciales o estado del arte hasta las explicaciones a nivel de funcionamiento y diseño de la herramienta. Bibliografía Akenine-Moller, T., Haines, E. y Hoffman, N. Real-time rendering . AK Peters/crc Press, 2018. Bailey, M. y Cunningham, S. Graphics shaders : theory and practice. CRC Press, 2012. Borromeo, N. A. Hands-On Unity 2020 Game Development: Build, customize, and optimize professional games using Unity 2020 and C#. Packt Publishing, 2020. Cookie, C. Get ready for eevee: Blender’s new real- time rendering engine. https://cgcookie.com/posts/ get-ready-for-eevee-blender-s-new-real-time-rendering-engine, 2018. Último acceso: Marzo de 2024. Davidović, D. Correccion gamma y por que es im- portante. https://gamedevelopment.tutsplus.com/es/ correccion-gamma-y-por-que-es-importante--gamedev-14466a, 2014. Último acceso: Abril de 2024. Dijkman, M. Exporting and importing with Blender and Unity. https:// mauritsdijkman.com/exporting-and-importing-with-blender-and-unity, 2021. Último acceso: Marzo de 2024. Gambetta, G. Computer graphics from scratch : a programmer’s introduction to 3D rendering . No Starch Press, 2021. Han, J. Introduction to Computer Graphics with OpenGL ES . CRC Press, 2018. Hoffman, N. Background: Physics and math of shading. https://blog. selfshadow.com/publications/s2013-shading-course/, 2013. Último acce- so: Mayo de 2024. Ilett, D. Building Quality Shaders for Unity: Using Shader Graphs and HLSL Shaders . Apress, 2022. 123 https://cgcookie.com/posts/get-ready-for-eevee-blender-s-new-real-time-rendering-engine https://cgcookie.com/posts/get-ready-for-eevee-blender-s-new-real-time-rendering-engine https://gamedevelopment.tutsplus.com/es/correccion-gamma-y-por-que-es-importante--gamedev-14466a https://gamedevelopment.tutsplus.com/es/correccion-gamma-y-por-que-es-importante--gamedev-14466a https://mauritsdijkman.com/exporting-and-importing-with-blender-and-unity https://mauritsdijkman.com/exporting-and-importing-with-blender-and-unity https://blog.selfshadow.com/publications/s2013-shading-course/ https://blog.selfshadow.com/publications/s2013-shading-course/ 124 BIBLIOGRAFÍA Matt Pharr, W. J. y Humphreys, G. Physically Based Rendering From Theory to Implementation , 4th edition. The MIT Press, 2023. Pai, H.-y. An imitation of 3d projection mapping using augmented reality and shader effects. En 2016 International Conference on Applied System Innovation (ICASI), páginas 1–4. IEEE, 2016. Sacco, M. Unity: Understanding URP, HDRP, and Built- In Render Pipeline. https://www.occasoftware.com/blog/ unity-understanding-urp-hdrp-built-in, 2023. Último acceso: Marzo de 2024. Sbalu. Principled bsdf for physically-based rende- ring in blender. https://fotorealist.com/blender/ principled-bsdf-for-physically-based-rendering-in-blender/, 2023. Último acceso: Abril de 2024. Shirley, P., Ashikhmin, M. y Marschner, S. Fundamentals of computer graphics . AK Peters/CRC Press, 2009. Shrestha, A. Render engines in blender: Eevee vs cy- cles - which one to choose? https://blog.yarsalabs.com/ render-engines-in-blender-eevee-vs-cycles-which-one-to-choose/, 2023. Último acceso: Marzo de 2024. Valenza, E. y Yamanoor, S. Blender Cycles: Materials and Textures Cookbook: Over 40 practical recipes to create stunning materials and textures using the Cycles rendering engine with Blender . Packt Publishing, 2015. https://www.occasoftware.com/blog/unity-understanding-urp-hdrp-built-in https://www.occasoftware.com/blog/unity-understanding-urp-hdrp-built-in https://fotorealist.com/blender/principled-bsdf-for-physically-based-rendering-in-blender/ https://fotorealist.com/blender/principled-bsdf-for-physically-based-rendering-in-blender/ https://blog.yarsalabs.com/render-engines-in-blender-eevee-vs-cycles-which-one-to-choose/ https://blog.yarsalabs.com/render-engines-in-blender-eevee-vs-cycles-which-one-to-choose/ Capı́tulo 10 Introduction “If you can’t give me poetry, can’t you give me poetical science?” — Ada Lovelace 10.1. Motivation The process of creating artistic content for video games involves collaboration between different professional sectors, including artists and programmers. However, this collaboration can encounter significant challenges due to differences in the tools and file formats used in each sector. A particular difficulty arises when transferring materials from the modeling software known as Blender, into game engines like Unity. The motivation for this project is to address this problem and improve the work- flow for video game artists and developers. Currently, the process of exporting ma- terials from Blender to Unity most likely involves a loss of information and visual quality. The current process results in a visual discrepancy between what the ar- tist creates and what is displayed in the final product. Furthermore, this process consumes a significant amount of time and is highly tedious. The decision to develop a tool for Blender that allows exporting materials to a Unity-compatible format stems from the desire to facilitate the transfer of content between the two aforementioned platforms. This new tool will help artists to main- tain the visual appearance of their creations in the final product while simplifying the export process. This will not only help improve visual quality but also optimize the artists’ workflow by reducing the need for manual adjustments. On the other hand, it will also optimize the work of programmers, who will receive content in format that is specialized and compatible with their platform. This tool has the potential to improve communication between artists and de- 125 126 Capítulo 10. Introduction velopers. By ensuring that materials created in Blender are transferred accurately and efficiently to Unity, the need for programmers to apply corrections and adjust- ments will be reduced, thereby decreasing their workload and level of frustration. This will allow for smoother and more efficient collaboration between both groups, as they can focus on their primary tasks instead of dealing with compatibility and file transfer issues. 10.2. Objectives The goal of this project is to design and implement a tool that is compatible with Blender, and is capable of exporting objects from a Blender scene to a Unity scene, including the materials that have been assigned to these objects, thereby obtaining materials and shaders compatible with Unity. The aim is to reach a point where the tool can export the most commonly used materials in Blender, and a user manual will also be incorporated, clearly indicating the tools requirements and limitations. To achieve this goal, it will be necessary to accomplish a series of intermediate goals, starting with the study of the rendering process in video games and how these processes are used in the platforms involved in our project, these being Blender and Unity. It will also be necessary to study the most commonly used materials in Blender and how to interpret them appropriately in Unity. Finally, thanks to these studies, we will be able to develop the tool as an add-on for Blender and conduct a series of usage evaluation tests for it. 10.3. Work Plan For the development of this project, the Scrum agile development methodology will be used, as it is flexible and easily adapts to the needs of the team. Tasks will be divided into user stories assigned to team members based on their availability or prior knowledge of the subject. To properly track progress, weekly meetings will be held, similar to how they were conducted during courses such as Proyectos. The main purpose of these meetings is to inform the team of the progress made and to reorganize tasks if necessary. Meetings with the tutors will generally take place every two weeks, with excep- tions during weeks where progress has been low. These meetings will be used to update the tutors on the progress made, to seek technical advice on specific areas, and to receive advice on getting the work on track. The first point investigated by the team will be the existing environment in the applications we will be working on: Blender and Unity. An in-depth investigation will 10.4. Document Structure 127 be conducted on the existing rendering technology in these platforms, how handle materials, the way that lighting works on each one, and other key points in the early stages of the project to base later decisions on solid knowledge foundations. Once this investigation is completed, the tool will be implemented as an add- on for Blender users, which they will be able to easily run from their computers. This tool will be versatile and adaptable to most materials that users can create in Blender. Since Blender has over a hundred different material nodes, each with various configuration modes and variations, to narrow the scope of the project, research will be conducted to identify the most commonly used features by users and implement these features in the tool. The development will be carried out on the GitHub repository available at the following URL: https://github.com/TFG-MEP/Blender_to_Unity_Material_ Export_Plug-in Finally, since the objective of the project is to provide a useful tool for developers, user testing will be conducted to ensure its proper functionality. The purpose of these tests is to gather user feedback on the tool’s usage and identify any errors, thus allowing for their resolution and making the tool as user-friendly as possible for potential users. 10.4. Document Structure In Chapter 2 Introducción al Renderizado, the processes required to understand this project are described in depth and in detail. In Chapters 3 and 4, the prior research related to the programs used for creating the tool is described in detail, as well as the rationale behind the decisions made for its adecuate implementation. In Chapter 5 Diseño de la herramienta, the current method for exporting ma- terials from Blender to Unity is discussed, followed by a description of the tool’s functionality and design. To support the design decisions made, this chapter inclu- des a survey on the use of materials in Blender. In Chapter 6 Implementación del add-on, the implementation process of the tool, as designed in the previous chapter, is explained in depth. The export process carried out by the tool is presented, and a series of examples are shown in order to visualize the obtained results. In Chapter 7 Pruebas de evaluación con usuarios, the different testing methods and the tests themselves are described. These tests have evaluated the tool’s usage and verified its correct functionality when used by different users. https://github.com/TFG-MEP/Blender_to_Unity_Material_Export_Plug-in https://github.com/TFG-MEP/Blender_to_Unity_Material_Export_Plug-in Capı́tulo 11 Conclusions and Future Work Conclusions and future lines of work. This chapter contains the translation of Chapter 8. El objetivo de este capítulo es exponer las conclusiones derivadas del pro- ceso de desarrollo de este proyecto, además de establecer posibles objetivos futuros que permitieran ampliar la herramienta obtenida. 11.1. Conclusions The developed project has resulted in an add-on aimed at facilitating the process of exporting 3D materials and models from Blender to Unity. The tool significantly reduces the time of the typical export process carried out by developers currently. This has the potential to become of great help for artists, eliminating the need of baking their materials, and allowing them to modify them easily in Unity. The development of this add-on required an exhaustive study of computer graphics concepts. The functioning of the rendering pipeline in both Unity and Blender was also studied, with particular emphasis on the different rendering engines and pipe- lines they offer, and their various uses. This study led to the conclusion that the Eevee rendering engine would be used in Blender, and the Universal Render Pipe- line in Unity. This has helped to fully understand the rendering process, the way shaders and materials work in a 3D environment, as well as the reasoning behind many problems that usually arise during material creation and exportation. The research on shader programming in Unity, using HLSL, was also particularly relevant. Thanks to this, it was possible to translate Blender materials into Unity’s .shader files. To achieve this, research on Blenders source code was also necessary, focusing on the way Blender manages and internally represents material nodes. The add-on implements support for fourteen different Blender nodes, enabling the export of .shader, .material, .fbx, and .prefab files, along with their respective 129 130 Capítulo 11. Conclusions and Future Work .meta files. Its design facilitates the straightforward incorporation of additional nodes for developers wishing to expand the tool. With all this considered, we can conclude that the results obtained have been satisfactory, meeting the initially set objectives and providing a solid solution to the problem of the lack of flexibility when exporting materials from Blender to Unity. Finally, to evaluate the effectiveness and correct functioning of the tool, user tests were conducted.These tests, despite bringing to light small inaccuracies in the tool, allowed us to corroborate the proper functioning of the add-on and the satisfaction of potential users with it. 11.2. Future Work Although the developed tool meets our initial expectations, there are several extensions that could be incorporated to enhance its integration into the regular Blender workflow. These extensions are outlined below as potential future work. Firstly, it would be optimal to expand the existing nodes in the tool. Some of the implemented nodes have multiple configurations that allow for new visual results, such as different functions for the Voronoi Texture node or the various blending functions of the Mix node. As future work, these different configu- rations could be studied and added to the add-on. Thanks to the modular structure with which the tool was developed, this extension would require only minor modifications to the existing code, being the principal addition the integration of these functions into the HLSL language. As indicated by users during testing, the add-on would significantly improve if it offered support for processing more types of material nodes. Notably, nodes mentioned in surveys but not implemented, such as the Noise Texture node or the Bump node. Again, thanks to the modular development approach used in the add-on, integrating these nodes into the export process would be possible without altering the code architecture. Future work would involve studying these new types of nodes and correctly interpreting them in a Unity shader. This would entail creating the corresponding HLSL struct and function for each new type of node. Another potential extension for future work would be to enable the tool to export the entire Blender scene. This way, all objects in the scene would be exported instead of just the one selected by the user. Additionally, this exten- sion would include the capability to export lights and the camera or cameras from the Blender scene to Unity. This process would not be trivial, as it would require an in-depth study of how these elements are interpreted and represen- ted on both platforms, as well as how to translate them properly from Blender to Unity, maintaining as much visual and functional correspondence as pos- sible. In any case, this extension would be an additional option for the user 11.2. Future Work 131 when exporting, allowing them to still execute the current process, exporting only the selected object. Finally, it would be interesting to work on the Volume and Displacement la- yers of Blender materials. This would not only allow for a greater variety of materials to be represented in Unity but also remove the restriction of not being able to use these layers in the materials exported by the tool, which can be very limiting for some Blender users. This extension would involve a thorough study of how these layers function and how to interpret them co- rrectly in a Unity shader, as well as expanding the add-on to integrate this new functionality into the existing code. Apéndice A Blender to Unity Material Export Add-On Installation and Usage Guide A.1. Installation Guide Open Blender. Go to “Edit” and then “Preferences”. Select “Add-ons”. Choose the “Install...” option and navigate to the directory where you down- loaded the repository’s content. Select the downloaded folder and click on “Install Add-on”. Activate the add-on by selecting the checkbox next to its name. A.2. Using the Add-On Select an object from your Blender scene layout. You should see a tab labeled “Generate Material” in the vertical toolbar. Click on that tab. In this tab, you will see a checkbox. Check it or uncheck it in order to choose whether to generate just the material and shader files, or these files along with the prefab and FBX files. Click on the button labeled “Generate Material”. 133 134 Apéndice A. Blender to Unity Material Export Add-On Installation and Usage Guide A new window will pop up for you to select a directory in your file system. This path will be where all the files are generated. Página de Título Dedicatoria Agradecimientos Resumen Abstract Índices Tabla de Contenidos Índice de figuras Introducción Motivación Objetivos Plan de trabajo Estructura de la memoria Introducción al Renderizado Elementos de una escena Objetos Cámara Iluminación Viewport Formas de renderizado Basado en luz Basado en objetos Materiales e interacción con la luz Normales Sombreado Modelo de iluminación Texturas Renderizado basado en físicas y función de distribución de dispersión bidireccional Pipeline gráfico Shader Programación de shaders Pipeline gráfico deferred Ventajas y desventajas de deferred y forward rendering Blender Objetos en Blender Materiales en Blender Creación de materiales Nodo Principled BSDF Motores de Renderizado Cycles Eevee Similitudes y diferencias entre Eevee y Cycles Add-ons en Blender Unity Renderizado en Unity Universal Render Pipeline High Definition Render Pipeline Objetos en Unity Materiales en Unity Shaders en Unity Modelos de shaders predefinidos Propiedades de los materiales en Unity Archivos .meta y GUID Diseño de la herramienta Estado actual de la exportación de materiales de Blender a Unity Descripción funcional de la herramienta Uso de los nodos de Blender Implementación del add-on Generación del archivo .shader Plantilla .shader Recorrido de los nodos Instanciación de los nodos en la plantilla Transparencia y Corrección Gamma Generación de archivos de imagen Generación del archivo .mat Generación del archivo .fbx Generación del archivo .prefab Empaquetado del Add-on Resultados Pruebas de evaluación con usuarios Objetivo de las pruebas Requisitos para los participantes Métodos de prueba Pruebas realizadas Conclusiones y Trabajo Futuro Conclusiones Trabajo Futuro Contribuciones Personales Bibliografía Introduction Motivation Objectives Work Plan Document Structure Conclusions and Future Work Conclusions Future Work Blender to Unity Material Export Add-On Installation and Usage Guide Installation Guide Using the Add-On