¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

«Love and Deep Space», un juego móvil interactivo de romance inmersivo en 3D y de estilo surrealista, ha superado los 70 millones de jugadores en todo el mundo desde su lanzamiento el 18 de enero de 2024 y ha sido galardonado con el premio al mejor juego para móviles en la Gamescom 2025.

En el evento Unite Shanghai 2025 celebrado hoy, los ingenieros de motores gráficos de «Love & Deep Space» han compartido por primera vez información técnica detallada, desvelando los entresijos de la creación del juego y explicando cómo, gracias a un proceso de renderizado de calidad cinematográfica y a efectos físicos realistas y minuciosos, consiguen crear un mundo realista y envolvente para los jugadores.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△La sala estaba llena hasta los topes durante la presentación

Se convirtió en la sesión más popular de la Unity Conference de este año

En Diezhi, compartir y crear forman parte del día a día de nuestro trabajo. Durante el último año, Diezhi ha organizado cerca de 40 sesiones de intercambio internas, ¡con una participación acumulada de 5.000 empleados!¡De ellas, más del 50 % fueron charlas técnicas! Los ponentes procedían de distintos equipos de producción y del departamento técnico, y los contenidos abarcaban experiencias prácticas y exploraciones de vanguardia en diversos ámbitos. Gracias a un ambiente de intercambio abierto, la profesionalidad y las ideas fluyeron libremente, lo que estimuló al máximo la creatividad.

A continuación se ofrece un resumen completo del contenido de la sesión técnica sobre «Love and Deep Space»:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

El contenido compartido sobre el marco de renderizado subyacente de «Love and Deep Space» abarca principalmente tres partes: [optimización del renderizado de escenas, soluciones de iluminación y diseño de flujos de trabajo, y optimización de sombras].Durante el proceso de desarrollo, hemos realizado modificaciones profundas en el código fuente del motor basándonos en Unity 2019, y hemos desarrollado un flujo de trabajo SRP personalizado. Actualmente, la versión en línea para Android utiliza GLES 3.1, aunque en el futuro también se lanzará una versión con Vulkan, con el fin de seguir mejorando el rendimiento y satisfacer las expectativas de los jugadores en cuanto a juegos de alta calidad.

1. Optimización del renderizado de escenas

En el marco de la optimización del renderizado de escenas, hemos desarrollado un sistema de renderizado de escenas denominado «RendererGroupRenderer», en el que cada lote de renderizado se denomina «RenderGroup». Mediante este sistema, hemos implementado las siguientes funciones:

  • Descripción de la escena estática personalizada: hemos eliminado los GameObjects para evitar la pérdida de rendimiento que supone actualizar un gran número de ellos.
  • Optimización de la frecuencia de carga de la CPU a la GPU: incluye principalmente la carga de InstanceData y ConstantBuffer. En cuanto a la optimización de la carga de InstanceData, en la fase inicial del proyecto, para escenas interiores a pequeña escala, utilizamos la generación estática de InstanceDataBuffer, combinada con el recorte mediante segmentación BVH.A medida que avanzaba el proyecto, los requisitos de precisión de las escenas aumentaron constantemente, por lo que en una fase posterior pasamos a realizar el recorte y el relleno de instancias en la GPU. La optimización de la carga de ConstantBuffer se explicará en detalle más adelante, en la sección «Optimización del rendimiento de una sola llamada de dibujo» (DrawCall).
  • Recorte aproximado en paralelo mediante el sistema «Burst+Job» en la CPU: para los objetos estáticos, hemos implementado un sistema de recorte de alta concurrencia mediante el sistema «Burst+Job», en el que el recorte aproximado se realiza únicamente en la CPU, mientras que las tareas de recorte de mayor precisión se delegan a la GPU.

Formato de datos de InstanceData

El uso habitual en el sector de InstanceData en formato de búfer constante (Constant Buffer) presenta algunos inconvenientes, como el límite de tamaño de 64 KB, el reducido tamaño del búfer constante y la facilidad con la que se producen desbordamientos de caché durante la indexación dinámica, lo que provoca una disminución del rendimiento.Otra forma habitual es utilizar SSBO para transmitir InstanceData, pero el rendimiento de lectura de este método suele ser inferior al del ConstantBuffer cuando no se produce un agotamiento de la caché; además, algunos dispositivos Android no admiten la lectura de SSBO en el sombreador de vértices bajo GLES, lo que también limita su compatibilidad.Además, ambas soluciones comparten un problema común: dependen de la indexación dinámica, lo que no resulta adecuado para el rendimiento de los teléfonos de gama baja.

Para abordar los problemas mencionados, proponemos una solución que podría describirse como «vino viejo en botella nueva»: los datos de instancia basados en Vertex Stream.

  • Utilizar el flujo de vértices del paso «PerInstance» como búfer de instancias;
  • Utiliza la caché de Vertex Fetch, no requiere índices dinámicos, tiene una alta tasa de aciertos en la caché y no presenta problemas de compatibilidad;
  • Se realiza la salida al Instance VertexBuffer a través de ComputeShader, lo que permite un procesamiento impulsado por la GPU.

Utilizar el flujo de vértices (Vertex Stream) de PerInstance Step como búfer de instancias (Instance Buffer): se trata de un método de instanciación compatible desde los inicios de la instanciación en la GPU, que permite evitar tanto los problemas de rendimiento derivados de la indexación dinámica como los problemas de compatibilidad con SSBO.También podemos implementar un sistema «GPU Driven» con un alto nivel de compatibilidad con GLES mediante la salida al búfer de vértices de instancia a través de ComputeShader.

Por último, dado que el motor Unity no admite de forma nativa el flujo de vértices «PerInstance Step», hemos realizado las adaptaciones necesarias en el motor, de modo que, finalmente, lo que se expone a las capas superiores es una interfaz «DrawMeshInstancedTraditional» añadida al CommandBuffer, que requiere que se le pase otra malla como datos de instancia.También hemos añadido las interfaces correspondientes para configurar la semántica de los vértices que corresponden a cada segmento de datos de la malla de instancias.

Impulsado por GPU

En función del número de grupos y de instancias, asignaremos por adelantado el búfer de parámetros indirectos y el búfer de datos de instancias (en este caso, el búfer de datos de instancias solo se asigna por adelantado; los datos reales se introducirán cuando la GPU realice el culling).

Al mismo tiempo, calcularemos previamente el desplazamiento de instancia (Instance Offset) de cada grupo y lo almacenaremos en el elemento InstanceStart del parámetro, vinculando únicamente un único búfer de instancia durante todo el proceso.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Además, necesitamos generar un búfer con información de cada objeto (que incluya el GroupID, el rango de distancia LOD, los límites y la transformación, entre otros datos), que se utilizará para obtener las propiedades de cada objeto durante el recorte en la GPU.

  • Recorte en la CPU: antes de realizar el recorte en la GPU, llevamos a cabo un recorte aproximado en la CPU para determinar si el grupo en su conjunto es visible.Partiendo de una caja envolvente raíz, comparamos la relación entre el volumen total de las cajas envolventes de los objetos y el volumen de la caja envolvente fusionada; si el resultado es inferior al umbral, dividimos la caja envolvente de forma recursiva (el objetivo principal es evitar que, debido a una distancia excesiva entre dos objetos, se genere una caja envolvente global excesivamente grande).Al mismo tiempo, combinamos esto con el PVS para determinar con mayor precisión la visibilidad del grupo. Dado que no disponemos de una función similar a IndirectExecute de DX12, nuestro recorte en la GPU solo puede reducir el número de instancias, pero no eliminar las llamadas de dibujo del grupo en su conjunto; por lo tanto, es necesario eliminar con la mayor precisión posible, mediante el recorte en la CPU, los grupos que sean completamente invisibles.
  • Recorte por GPU: El recorte por GPU realiza, mediante un único despacho, un recorte de tres etapas para cada objeto de todos los grupos, lo que incluye el recorte de la cónica de visión, el recorte de LOD y la eliminación de oclusiones Hiz. Mediante el recorte, se incrementa en 1 el recuento de instancias del parámetro y se generan los datos de instancia.
  • Eliminación de sombras: hemos seguido el método compartido en «Dragon’s Dogma» y hemos reproyectado la profundidad de la imagen en el espacio de sombras para crear una «Shadow Reveiver Mask»; si el volumen proyectado por el «Shadow Caster» no se cruza con la máscara, se puede eliminar para evitar el renderizado de sombras innecesarias.

Además, en cuanto a la sección «¿Por qué no hemos implementado Cluster/Meshlet?», en primer lugar, supone una carga básica considerable en dispositivos móviles y, en segundo lugar, la implementación de Cluster en GLES plantea problemas de compatibilidad. Tras analizar todos estos factores, consideramos que dar prioridad a la optimización del rendimiento de cada DrawCall nos reportará una mejora de rendimiento inmediata y sin coste adicional.

Optimización del rendimiento de una sola llamada de dibujo (DrawCall)

En observaciones anteriores, hemos constatado que muchas optimizaciones del tiempo de procesamiento de la CPU en el renderizado suelen centrarse excesivamente en el número de llamadas de dibujo (DrawCall), pasando por alto el tiempo que tarda cada llamada de dibujo en sí. Consideramos que reducir el número de llamadas de dibujo es solo un método de optimización, y que el tiempo de procesamiento final de la CPU es el único indicador relevante.

Los dispositivos móviles modernos y los estándares gráficos son, de hecho, capaces desde hace tiempo de gestionar un gran número de drawcalls, algo que el equipo del motor HypeHype ya compartió en Siggraph 2023: realizaron pruebas con 10 000 drawcalls de diferentes mallas y texturas en un iPhone 6s, y el tiempo de ejecución fue de tan solo 11,27 ms.Otros dispositivos Android equivalentes también suelen mantener una tasa de fotogramas superior a 60. Y en 2014, cuando Metal acababa de ver la luz, ya se había planteado el objetivo de realizar 10 veces más drawcalls que GLES.

Hoy, 11 años después, seguimos teniendo problemas con el exceso de DrawCalls debido principalmente a diversas fuentes de consumo de recursos, entre las que se incluyen los cambios excesivos de PSO, el envío y la copia de búferes, la lógica de renderizado del motor y el exceso de llamadas a la interfaz RHI, todo lo cual aumenta la carga de la CPU.Por lo tanto, consideramos que la optimización del rendimiento no debe centrarse únicamente en el número de DrawCalls, sino que debe tener en cuenta todos estos factores de forma integral.

  • Optimización del cambio de PSO: depende principalmente del equilibrio entre el número de variantes de shader y la complejidad de los mismos en cada proyecto.La cola de renderizado de RenderGroup se ordena según la prioridad de los shaders, los materiales y las mallas; al mismo tiempo, aplicamos un tratamiento especial a las sombras: los materiales sin AlphaTest utilizan un mismo shader para renderizar la profundidad de las sombras, lo que reduce la frecuencia de los cambios de PSO durante el renderizado de las sombras.
  • Optimización del envío de búferes: en GLES, las operaciones de asignación y desasignación de búferes suponen una sobrecarga significativa. Aunque la asignación persistente que admite el RHI moderno reduce considerablemente el tiempo de carga, sigue sin poder evitar las múltiples copias de datos del hilo principal al hilo de renderizado y, posteriormente, a la memoria del búfer, así como las operaciones memcmp.Por ello, hemos adoptado las tres estrategias específicas siguientes, que han reducido notablemente la carga de la carga del búfer:
    • PerRendererBuffer almacena los parámetros de cada Renderer (como la luz ambiental SH que recibe el objeto) en un Uniform Buffer gestionado por el objeto Renderer, y se vincula directamente durante el renderizado;
    • PerShaderBuffer está pensado para los búferes uniformes que no necesitan cambiar para cada material, y solo se envía una vez al cambiar de shader. En comparación con PerRendererBuffer, PerShaderBuffer es más flexible y admite diferentes variantes de shader;
    • En el caso de PerMaterialBuffer, hemos utilizado el código de SRP Batcher para generar previamente los buffers por material y vincularlos directamente.
  • Optimización de la lógica de renderizado: para garantizar la flexibilidad y la estabilidad, los motores de videojuegos comerciales realizan complejas evaluaciones lógicas durante el renderizado. Por ejemplo, en el motor Unity, cada vez que se invoca a «Draw», se llama primero a una función llamada «ApplyMaterial», que actualiza todos los estados y parámetros de renderizado antes de que este se lleve a cabo; cuando el número de «DrawCalls» es elevado, esto supone un tiempo de ejecución considerable. Por ello, hemos llevado a cabo las siguientes optimizaciones:
    • La interfaz ApplyMaterial se ha separado de forma independiente, de modo que solo la capa superior la invoca de forma activa cuando es necesario cambiar el material o los parámetros;
    • Solo cuando se modifique PerMaterialBuffer, utilice la interfaz específica simplificada.

Tras la optimización, nuestra CPU ha reducido en un tercio el tiempo de ejecución con el mismo número de DrawCall.

  • Optimización de las llamadas a RHI: El objetivo principal de la optimización de las llamadas a RHI es reducir las llamadas a la API gráfica, salvo las de «Draw Primitive». Las optimizaciones concretas incluyen:
    • Fusionar los búferes de vértices e índices con el mismo stride, evitando tener que vincular el VB y el IB en cada llamada de dibujo, lo que reduce el tiempo de ejecución en un 15 %;
    • Cuando el recurso no ha cambiado, se omite la configuración del DescriptorSet, lo que reduce aún más el tiempo de ejecución en un 30 %; cuando la operación SetDescriptors en sí misma requiere mucho tiempo y, además, el cambio de descriptor aumenta el tiempo de ejecución del siguiente dibujo, tal y como se explica en la Guía de buenas prácticas de Arm.

Hemos probado el tiempo de ejecución de 5.000 DrawCalls en dispositivos Android de gama baja. Al utilizar el renderizado nativo del motor, el tiempo de ejecución del hilo de renderizado fue de 34,79 ms.Tras optimizar el envío al búfer y la lógica de renderizado, el tiempo de ejecución se redujo a 22,97 ms. Al optimizar aún más el número de llamadas a RHI, el tiempo de ejecución se redujo considerablemente hasta los 11,8 ms. Finalmente, sin modificar el número de DrawCall, conseguimos reducir el tiempo de ejecución de la CPU a menos de un tercio del original.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Resultados de las pruebas de rendimiento en escenarios de referencia de otros intentos de optimización

También hemos probado algunas nuevas funciones de RHI, entre las que se incluyen:

  • Multi-Draw Indirect (MDI): ofrece una optimización notable en los dispositivos compatibles y, en cierta medida, mejora el problema de que la eliminación de objetos ocultos por la GPU pueda generar llamadas de dibujo vacías (reducción de las llamadas enviadas desde la CPU);
  • Bindless: Sin embargo, el rendimiento de Bindless no ha sido el esperado, ya que incluso en los dispositivos Android más recientes se han producido misteriosas «optimizaciones negativas». Combinando MDI con Bindless, podemos renderizar prácticamente todos los objetos con una sola llamada de dibujo (DrawCall), pero el tiempo de procesamiento de la CPU es incluso mayor que cuando no se agrupan en lotes. Este es también un ejemplo negativo de prestar demasiada atención al número de llamadas de dibujo.Por supuesto, esperamos que los futuros chips para dispositivos móviles ofrezcan una mejor compatibilidad con Bindless. Por el momento, hemos intentado desarrollar, a partir de Unity Texture Streaming, un sistema SVT sin retroalimentación como alternativa, aunque esta solución aún se encuentra en fase de validación.

A juzgar por los resultados de las pruebas de escenarios de referencia, RenderGroupRenderer, en comparación con el renderizado original sin instanciación, redujo las llamadas de dibujo en un tercio,el tiempo de ejecución de los hilos de renderizado se ha reducido considerablemente en tres cuartas partes, y el tiempo de ejecución del hilo principal también se ha reducido en dos tercios (aunque el tiempo de ejecución en C# ha aumentado, el tiempo dedicado al recorte nativo del motor y a la actualización de GameObjects se ha reducido, lo que, en conjunto, sigue suponiendo una optimización considerable).

2. Plan de iluminación

Plan de iluminación

  • Proceso de renderizado directo:

En el proyecto hemos optado por utilizar el pipeline de renderizado directo, teniendo en cuenta varios aspectos: en primer lugar, el pipeline directo presenta ventajas a la hora de hacer frente a las complejas y variables necesidades artísticas, ya que no tenemos que preocuparnos de si la incorporación de ciertas propiedades de los materiales provocará un aumento del tamaño del GBuffer.

Por otra parte, las tuberías de retardo tradicionales no son muy adecuadas en cuanto al ancho de banda para las plataformas móviles. OnePassDeferred, por su parte, presenta algunas limitaciones en cuanto a flexibilidad; por ejemplo, no permite modificar el tamaño del RT en medio del RenderPass, ni recuperar el contenido de píxeles más allá de la posición actual.

En GLES, la compatibilidad de FrameBufferFetch también plantea problemas: los distintos chips admiten un número diferente de RT de fetch; algunos solo admiten una RT, por lo que es necesario recurrir a PLS, pero las pruebas de rendimiento de PLS no han dado resultados satisfactorios.

Por otra parte, las cuatro fuentes de luz por objeto que incluye el motor no son suficientes para objetos de gran tamaño, por lo que probamos Forward+. Sin embargo, Forward+ consume demasiado tiempo en los dispositivos más antiguos; si se limita el número máximo de fuentes de luz por mosaico, al cambiar de plano no se puede controlar el número de fuentes de luz dentro del mosaico, y superar el límite máximo provoca errores de rendimiento.

Para resolver estos problemas, hemos adoptado una división del espacio del mundo en mosaicos horizontales —con un tamaño predeterminado de 2 metros por mosaico—, distribuidos delante de la cámara, con un máximo de 4 fuentes de luz por mosaico y un mapa de índices de 128 × 128. Este método de división garantiza la estabilidad de la superposición de las fuentes de luz entre mosaicos, lo que facilita la detección oportuna de problemas de sobrepaso de límites durante la producción.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!
  • Mejoras en el proceso de gestión de versiones de Vulkan

Hemos incorporado un «Light Pre-Pass» basado en Subpass en el pipeline de las futuras versiones de Vulkan.

En el Pre-Z Pass, generamos un GBuffer RT simplificado y lo almacenamos.Dado que nuestra iluminación local utiliza un modelo PBR simplificado sin Fresnel, no necesitamos generar especular ni albedo en el GBuffer, sino que solo empaquetamos la normal, la rugosidad y algunos ID de material o información de atributos específicos en un GBuffer RGBA8,A continuación, podemos ejecutar un proceso de renderizado de volumen de iluminación similar al del sombreado diferido y guardar los resultados de la iluminación geométrica en la memoria de mosaicos.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Posteriormente, en la fase de sombreado (Shading Pass), volveremos a dibujar el objeto y recuperaremos esa información de iluminación; a continuación, la combinaremos con las propiedades de los materiales, como el albedo, obtenidas durante el renderizado, para obtener el resultado final de iluminación.

Codificaremos el MotionVector necesario para TAA en RGBA8; R + G == 0 indica que no hay velocidad válida, de modo que algunos materiales que no emiten velocidad puedan almacenar otra información en el canal BA.

Por ejemplo, en el caso de vegetación sencilla y abundante, guardamos su información UV en el canal BA de MotionVector; de este modo, durante la pasada de sombreado, solo tenemos que realizar un posprocesamiento para obtener la información geométrica del gbuffer y la información UV de MotionVector, con lo que podemos recrear el aspecto del material de la vegetación.

El flujo del pipeline en la versión de Vulkan es, a grandes rasgos, el siguiente: en primer lugar, el PreZ Pass genera la profundidad (Depth), el búfer de geometría (GBuffer) y el vector de movimiento (MotionVector); a continuación, se calcula el culling de sombras,a continuación, se lleva a cabo el renderizado de profundidad de las sombras; después, se realizan cálculos como el AO y el SSS en el espacio de pantalla; a continuación, se pasa al NativeRenderPass, donde, en el SubPass, se calculan la ShadowMask y el Light Pre-Pass, y se ejecuta el Shading Pass normal. Por último, se sale del RenderPass y se ejecutan otros pases de posprocesamiento.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Las mejoras en el pipeline de la versión de Vulkan también presentan ciertas limitaciones; por ejemplo, Light Pre-Pass solo puede sustituir al modelo de iluminación predeterminado, por lo que, para los modelos de iluminación que requieran más canales de Gbuffer, sigue siendo necesario utilizar Forward+.

No obstante, ofrecemos un parámetro seleccionable para cada fuente de luz, que permite forzar el uso del «Standard Lit Model» para una fuente de luz concreta y aplicar un tratamiento uniforme a todos los materiales; de este modo, aunque se sacrifique la precisión del modelo de iluminación, se consigue que los píxeles de un mismo mosaico se vean afectados por más de cuatro luces.

  • Buenos días

En la parte de GI difusa, hemos adoptado el método más tradicional de «lightmap + light probe», en el que el lightmap solo almacena información sobre la luz indirecta,en cuanto a las sondas de luz, además del modo habitual de un único punto de muestreo por objeto, ofrecemos un modo de muestreo múltiple que permite establecer varios puntos de muestreo para cada objeto, realizando la interpolación en función de las coordenadas del centro de gravedad de los segmentos, triángulos o tetraedros.

En las dos imágenes comparativas siguientes, la de la izquierda muestra el resultado con un único punto de muestreo, con una iluminación ambiental uniforme en la parte inferior del recuadro; la de la derecha, en cambio, muestra el resultado obtenido con dos puntos de muestreo, en el que se puede observar que los lados izquierdo y derecho reciben una iluminación indirecta diferente.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

En cuanto a la GI especular, nos basamos principalmente en sondas de reflexión (Reflection Probe) corregidas con AABB. Además, para determinados suelos o superficies de agua, también utilizamos agentes de reflexión planos. En términos generales, se puede considerar como un HLOD específico para renderizar reflejos.

Además, siguiendo el ejemplo de God of War, hemos normalizado el CubeMap de Reflection Probe. En concreto, generamos un coeficiente SH de iluminación ambiental en función de los píxeles del CubeMap y dividimos el color de cada píxel del CubeMap por la iluminación ambiental en esa dirección, obteniendo así un CubeMap normalizado.Durante el renderizado propiamente dicho, se multiplica cada píxel por la iluminación ambiental real que recibe en la dirección de reflexión, con el fin de reconstruir el color del reflejo.

La ventaja de este método es que, aunque un gran número de objetos muestreen la misma sonda de reflexión, los reflejos de distintas zonas pueden generar diferentes matices de luz y sombra.

3. Optimización de las sombras

Diseño funcional

El diseño básico de nuestro sistema de sombras es el siguiente:

  • Sombras de primer plano de personajes de nivel 3 CSM+ / POSM para múltiples personajes: sombras de primer plano exclusivas de personajes de nivel 3 CSM+ y nivel 1 de Cascade; en determinadas escenas con múltiples personajes se utiliza POSM (Per-Object Shadow Map);
  • Admite la proyección de dos focos cónicos;
  • ScreenSpaceShadowMask: el resultado de todas las sombras anteriores se envía a una ScreenSpaceShadowMask de RGBA8;
  • R: Sombra direccional, G: Sombra local 1, B: Sombra local 2, A: AO: el canal R almacena las sombras de la luz principal, los canales G y B almacenan las sombras de las luces cónicas, y el canal A almacena la información de AO.

Exclusión por distancia

En primer lugar, realizamos una sencilla eliminación por distancia: modificamos los valores de profundidad de los vértices de los triángulos del posprocesamiento ScreenSpaceShadow en función de la distancia de la sombra y, a continuación, utilizamos ZTest Greater para renderizar y eliminar el cálculo de las sombras que se encuentran fuera de dicha distancia.

Dado que, al calcular las sombras, hay que muestrear la profundidad, necesitamos dos copias de la profundidad, una para «Test» y otra para «Sample». En «NativeRenderPass» copiaremos una copia del búfer de profundidad «Memoryless» para «Test», con el fin de evitar en la medida de lo posible un ancho de banda adicional de lectura y escritura.

Detección de la zona de penumbra

Hemos añadido una función de detección de zonas de penumbra: primero se calcula una vez el PCF a 1/4 de resolución y, a continuación, en el «Shadow Pass» a resolución completa se muestrea una máscara de 1/4; solo se aplica el PCF a resolución completa a los píxeles cuyos valores de sombra se encuentran en la zona intermedia, lo que permite reducir la carga computacional sin comprometer la calidad del resultado.

Para evitar que, tras realizar esto, surjan problemas de imprecisión en la detección de determinados píxeles, calculamos dos conjuntos de normales basándonos, por un lado, en la derivada de la posición en el búfer de 1/4 y, por otro, en los cuatro valores de profundidad del «Gather» a resolución completa. Si el ángulo entre las normales supera el umbral, se considera que el píxel de baja resolución no es fiable y se ejecuta de forma forzada el PCF a resolución completa.

A continuación se muestra la vista de depuración de la escena; hemos identificado la zona roja como el intervalo de penumbra, y solo en estos píxeles se ejecutará el PCF a resolución completa.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Sesgo por píxel

Hemos implementado el «Shadow Bias» píxel a píxel utilizando el algoritmo «Receiver Plane Depth Bias». Su principio es bastante sencillo: en primer lugar, se aplica la regla de la cadena bidimensional a las derivadas parciales de las coordenadas de sombra en el espacio de la pantalla para calcular las derivadas parciales en el espacio de las sombras.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Utilizando la derivada parcial y el desplazamiento de muestreo de la PCF, podemos calcular el valor del sesgo. Para el punto central, hemos añadido el resultado del sesgo con un desplazamiento de 1 píxel como sesgo inicial.

En la siguiente imagen se muestran los resultados de la comparación entre el sesgo fijo y el sesgo por píxel:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

En la imagen de la izquierda, al utilizar un valor de sesgo fijo, se observa una zona de fuga de luz en la parte inferior de la caja, así como un cierto distorsión en las sombras propias en las superficies casi perpendiculares a la dirección de la iluminación; tras aplicar un sesgo píxel a píxel (imagen de la derecha), solo aumentamos el sesgo en las zonas con mayor derivada, lo que permite resolver el problema de la distorsión de las sombras propias sin perder los detalles de la proyección.

Sin embargo, cuando la profundidad de la pantalla no es continua, el sesgo por píxel puede dar lugar a resultados erróneos, lo que provoca algunas fugas de luz. Para resolver este problema, es necesario que el diseñador gráfico especifique manualmente el rango mínimo y máximo del sesgo.

Mapa de sombras en caché con desplazamiento

En los casos en los que hay un gran número de DrawCalls, también hemos probado el «Scrolling Cached Shadow Map», que incluye concretamente:

  • Almacenar en caché la profundidad CSM: para los objetos que están completamente incluidos en el cono de sombra tanto en el fotograma anterior como en el posterior, se traslada el CSM del fotograma anterior a la posición de proyección del fotograma actual para obtener directamente la profundidad de la sombra, evitando así renderizar directamente el objeto;
  • El desplazamiento solo se aplica al último nivel de la cascada; cuando el alcance de la cascada es reducido, una gran cantidad de objetos se cruzan con el cono de visión, lo que limita el efecto de la optimización;
  • Actualizar la caché cada varios fotogramas para aliviar la carga sobre el ancho de banda.

En el futuro, también tenemos previsto incorporar el Local ShadowMap Atlas y un mecanismo de almacenamiento en caché. Ofreceremos soporte para la proyección de dos o más luces locales y ajustaremos dinámicamente la precisión de ShadowDepth en función de la proporción de la pantalla que ocupa la fuente de luz; además, para las fuentes de luz locales situadas a gran distancia, introduciremos soporte para el almacenamiento en caché estático.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

1. Esquema de iluminación de los personajes

En lo que respecta a los esquemas de iluminación de personajes, seguro que todos os habéis encontrado, en mayor o menor medida, con los siguientes tipos de problemas:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Si desglosamos estas cuestiones, podemos resumirlas en las tres necesidades siguientes:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Teniendo en cuenta las necesidades mencionadas, hemos diseñado un plan de iluminación específico para cada personaje.

La iluminación se compone de 【luz directa】 y 【luz indirecta】; por lo general, solo tendremos una luz paralela, a la que solemos llamar «luz principal». La luz principal ilumina la escena de forma normal, pero al iluminar a los personajes conservamos su dirección y sobrescribimos el color y el brillo de la luz principal utilizando un método similar al de una caja de posprocesamiento. La forma concreta de llevarlo a cabo es la siguiente:

  • Pasar al shader un valor adicional con el color de la luz principal del personaje; así, cuando el shader del personaje obtenga la luz principal, el color que reciba será el color de la luz principal del personaje;
  • Se ha añadido al personaje una luz paralela adicional que no proyecta sombras, para utilizarla como luz de contorno;
  • Al mismo tiempo, se han reservado dos luces adicionales para los personajes; estas luces adicionales pueden ser cualquier combinación de luces puntuales y focos, y pueden iluminar con normalidad a los personajes y los objetos de la escena que se encuentren dentro de su alcance (dado que en una casilla de 2 metros caben como máximo cuatro luces adicionales, se han asignado dos luces a los personajes).

Para la luz indirecta, utilizamos el sistema LightProbe de Unity para crear sondas; hemos implementado nosotros mismos la parte encargada de guardar la luz indirecta en las sondas, y hemos separado las sondas de la escena de las de los personajes en dos conjuntos distintos, que se almacenan y utilizan por separado;

Para los reflejos de la luz ambiental utilizamos la misma sonda de reflexión, pero, en el caso de algunos materiales especiales, ofrecemos la opción de introducir un mapa cúbico en el material para sustituir la sonda de reflexión ambiental.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Guardamos la información de iluminación que afecta a los personajes en un ScriptableObject y, una vez que el iluminador la haya ajustado, la guardamos como plantilla;La imagen inferior derecha muestra la información guardada del esquema de iluminación del personaje, que incluye las dos luces paralelas mencionadas anteriormente, dos luces adicionales, el shader guardado por la sonda, así como información que se puede ajustar adicionalmente en las cajas de posprocesamiento y la opción de utilizar sondas de reflexión personalizadas.

Por último, se utiliza un gestor que funciona de forma similar a una pila; la elección de este método de gestión está estrechamente relacionada con el uso concreto: normalmente, aparte de cargar nuevos esquemas de iluminación, una de las funciones más utilizadas es restaurar el efecto del esquema de iluminación anterior, por lo que hemos optado por el método de gestión mediante pila.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Llegados a este punto, este esquema ya cuenta con funciones como la separación de personajes y escenas, la posibilidad de cambiar en tiempo real y la compatibilidad con plantillas personalizadas. Por último, hemos definido el cambio de esquema de iluminación como un comportamiento de evento en el editor de guiones, lo que permite encadenar animaciones de iluminación.

Los efectos de luz que se pueden combinar son los siguientes:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

La siguiente imagen muestra la herramienta de edición de guiones específica del proyecto; básicamente, todos los parámetros relacionados con la iluminación y las sombras, así como parte del posprocesamiento y los efectos físicos, se pueden controlar desde este editor de guiones.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

2. Primer plano con sombras

La luz y la sombra siempre han estado indisolublemente unidas. Como se ha mencionado anteriormente, nuestro sistema de sombras consiste en un CSM de tres niveles más sombras de primer plano; el principio de funcionamiento consiste en utilizar un hueso especificable del personaje como centro de una esfera de un radio determinado, y emplear dicha esfera para construir y generar este mapa de sombras.al aplicar las sombras en el espacio de la pantalla, se realiza una comparación de precisión y se utiliza la imagen de sombra o la sombra en cascada que presente mayor precisión como mapa de sombras para ese píxel.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Como se puede ver en la siguiente animación, el personaje se encontraba originalmente por completo dentro de la sombra de la luz principal, pero al activar la sombra de primer plano, pasó a estar iluminado normalmente por la luz principal, precisamente porque la sombra de primer plano modificó el plano de recorte cercano;Es decir, nuestras sombras de primer plano son una imagen de sombra independiente con parámetros ajustables, entre los que se incluyen los planos de recorte cercano y lejano, la distancia máxima, la luz que se utiliza y el «bias», que suele ser lo que más quebraderos de cabeza causa.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

3. Detalles de la piel

En cuanto a la piel, nos centramos en algunos detalles, concretamente en los efectos del rubor y la sudoración.

  • Efecto de sonrojo

Por lo general, el proceso de sonrojarse es gradual y el grado de enrojecimiento varía según la zona; por ejemplo, a la mayoría de las personas, cuando se sonrojan, les enrojecen primero las orejas, luego las mejillas y, en ocasiones, se les enrojece todo el rostro.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Para simular este proceso, hemos adoptado las siguientes medidas con el fin de que las imágenes resulten más vívidas y realistas:

  • Máscara dibujada a mano: controla las zonas de rubor, los degradados de color y la intensidad a partir de una textura de máscara;
  • Canales independientes: permite ajustar por separado el efecto de rubor en diferentes zonas, como la cara, las orejas o la nariz;
  • Proceso de variación predefinido: el proceso de transición del rubor se registra por canales en las curvas correspondientes, lo que permite una expresión emocional natural.
  • Efecto de sudoración

En nuestro juego ofrecemos una función de acompañamiento deportivo: el protagonista acompaña a los jugadores en algunos entrenamientos, por lo que es necesario incluir los efectos correspondientes de sudoración. La implementación concreta se lleva a cabo principalmente a través de los tres aspectos siguientes:

  • Combinación de materiales y partículas: el sombreador de materiales simula el brillo y la humedad de la superficie de la piel; para el efecto de las gotas de sudor, se puede elegir entre una implementación basada en materiales adheridos a la piel o una implementación con efectos visuales (VFX);
  • Máscara para controlar las zonas de sudoración: utiliza una imagen de máscara para determinar las zonas de sudoración del material, lo que mejora el realismo y el carácter artístico del efecto de sudoración;
  • Transferencia automática de datos: los cambios en los parámetros principales determinan automáticamente los parámetros de los materiales y las partículas.

En la siguiente imagen se muestran algunos métodos de cálculo concretos y una ilustración del resultado final.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△ Calcular la posición en la que se forman las gotas de sudor y modificar la rugosidad de dicha posición

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△ Generación de números aleatorios mediante el identificador de rejilla UV

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△ Simulación de la trayectoria de una gota de sudor al caer

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△ Efectos del sistema de acompañamiento deportivo en la sudoración

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

La presentación sobre los efectos físicos se centró principalmente en cuatro aspectos: la implementación de la simulación de telas, el control de actuaciones en tiempo real, el desarrollo basado en Unity DOTS y el módulo de detección de colisiones.

1. Implementación de la simulación de tejidos

Para resolver algunos problemas específicos del proyecto, hemos desarrollado internamente un sistema de simulación de tejidos.

Sistema de simulación de telas basado en esqueletos: StrayCloth

StrayCloth utiliza un método de simulación que combina XPBD con subpasos. En comparación con PBD, la ventaja de XPBD es que no depende del número de iteraciones ni del paso temporal, y, al combinarse con los subpasos, puede mejorar significativamente la convergencia de la solución.

Lo que lo hace un poco especial es que utilizamos esqueletos como partículas simuladas, es decir, cada partícula, además de su posición, contiene información sobre su rotación.

En la implementación concreta de los subpasos, utilizamos un tiempo de subpaso dinámico, comprendido entre 1/200 y 1/300, en función de los distintos escenarios de exigencia de rendimiento. Además, realizamos una interpolación de movimiento de los objetos en movimiento de la escena, lo que hace que el efecto de las colisiones sea más estable.De hecho, aunque la interpolación del movimiento no supone una carga muy elevada en términos de rendimiento, su aplicación resulta bastante complicada en la práctica debido a la gran variedad de tipos existentes, como partículas estáticas, cuerpos de colisión o campos de viento, entre otros.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

¿Por qué se utilizan esqueletos en lugar de mallas proxy? Principalmente por las tres razones siguientes:

  • «Love and Deep Space» presenta requisitos complejos en cuanto a la trama, los combates y los cambios de vestuario, y el sistema de esqueletos permite una transición fluida entre las animaciones y el cálculo;
  • Dadas las exigencias de control y las limitaciones de rendimiento en dispositivos móviles, el sistema de esqueletos ofrece a los artistas un mayor margen de maniobra;
  • Utilizando esqueletos y restricciones se pueden construir estructuras similares a las mallas para conseguir un efecto parecido.

Esquema de sujeción ósea

En las soluciones existentes para el simulación de telas con esqueleto, las restricciones del esqueleto suelen implementarse mediante restricciones de forma locales y globales; aunque son sencillas y rápidas, presentan un inconveniente evidente: cuando se utilizan para la simulación de telas, el resultado tiende a un estilo de dibujos animados, lo que no se ajusta al estilo realista en 3D que persigue «Love and Deep Space»;Además, el ajuste de sus parámetros no resulta intuitivo, ya que cuenta con dos parámetros de intensidad de curvatura —global y local—, lo que dificulta el ajuste por parte de los artistas y la adaptación de los efectos a diferentes escenas.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Por lo tanto, en cuanto al esquema de restricciones esqueléticas, hemos optado por una restricción basada en la barra de Cosserat. Entre sus ventajas se incluyen:

  • El resultado es más natural y se ajusta al estilo artístico realista general de «Love and Deep Space».
  • El ajuste de los parámetros es más intuitivo y, al separarse la resistencia en los tres ejes, en algunos casos —como al simular una falda— se puede aproximar el efecto de un miriñaque mediante la resistencia a la flexión anisotrópica.
  • Se puede reutilizar directamente en la simulación del pelo, por lo que el pelo y la ropa pueden compartir un mismo conjunto de restricciones.

Para conocer los resultados concretos, puedes consultar el rendimiento de la última tarjeta diaria:

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

La relación entre el tejido y el personaje

La conexión entre el tejido y los personajes se establece principalmente de dos maneras:

  • Jerarquía: el esqueleto estático se ve directamente afectado por la animación ósea del personaje y se mueve según la relación jerárquica.
  • Este método es bastante sencillo y funciona bien en algunas zonas de unión que tienden a ser rígidas. Sin embargo, en posiciones más complejas, como el codo, el hombro o la cintura, donde la unión ósea está influida por varios huesos o donde se producen estiramientos y contracciones de cierta amplitud, es fácil que se produzca una separación entre el tejido y el personaje.
  • Adhesión: las partículas estáticas están controladas por los triángulos de anclaje del modelo de personaje. La malla se hornea en paralelo y se actualiza en cada fotograma mediante el cálculo de las coordenadas del centro de gravedad.

En los casos especiales en los que el triángulo presenta degeneración, utilizamos la transformación del esqueleto de piel de los vértices del triángulo para realizar un equilibrio ponderado con el fin de actualizar la transformación de las partículas estáticas.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Esquema de colisión

En cuanto al sistema de colisiones, utilizamos un BVH dinámico para gestionar la fase amplia de las colisiones de la escena; cada personaje se considera un subárbol que contiene sus propios cuerpos de colisión como nodos del subárbol.

Al mismo tiempo, gestionamos el uso compartido de las reglas de colisión entre diferentes roles y componentes mediante tres criterios: el ID del rol, la visibilidad compartida y el tipo de componente.

En la fase «narrow», no generamos contactos directamente, sino que almacenamos en caché los pares de cuerpos de colisión para resolverlos de forma concreta en el subpaso; gracias a las ventajas del subpaso que utilizamos, en la mayoría de los casos basta con utilizar directamente el DCD para evitar los problemas de penetración que se producen en movimientos rápidos, sin necesidad de introducir operaciones como el CCD o el contacto predictivo.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!
  • Implementación de Mesh Collider

En el caso de los colisionadores geométricos parametrizados, como «plane», «capsule» o «box», es relativamente sencillo resolver las colisiones entre estos y las partículas o los «edge». En zonas complejas como los hombros, el cuello, el pecho y la espalda, los colisionadores geométricos parametrizados tienen dificultades para representar con precisión la forma del modelo del personaje, lo que puede provocar fenómenos de penetración; por ello, en estas zonas utilizamos ampliamente los colisionadores de malla (Mesh collider).

Sin embargo, dado que el «mesh collider» es un cuerpo cóncavo irregular, en ocasiones puede no estar cerrado,lo que hace que lograr un efecto de colisión preciso resulte relativamente más difícil que con la geometría parametrizada, especialmente en dispositivos móviles. Por ello, utilizamos un algoritmo de hash como método de búsqueda aproximada de triángulos y, combinándolo con los resultados de los triángulos vecinos almacenados en caché, generamos un par de colisión partícula-triángulo antes de iniciar la iteración,en las iteraciones posteriores se comprueba si la partícula se encuentra dentro del ámbito del triángulo; si se sale de dicho ámbito, se realiza una búsqueda de triángulos con paso limitado, basándose en las relaciones de adyacencia de los triángulos del modelo, para obtener el triángulo más cercano, y se almacena el resultado en la caché para su uso en la siguiente iteración.

La animación que aparece a continuación muestra algunos ejemplos concretos del rendimiento del proyecto; como se puede observar, el rendimiento es bastante estable.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Face Collider

El colisionador facial puede considerarse un colisionador de malla especial; en comparación con el colisionador de malla básico, tiene una forma más fija y suave, y prácticamente no presenta superposiciones de triángulos partiendo del centro del modelo, por lo que utilizamos un mapa cúbico de 16×16 para precalcular los triángulos en todas las direcciones,de modo que, durante el cálculo de colisiones, se puedan localizar rápidamente los triángulos adyacentes.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Colisión entre capas

La simulación de colisiones entre telas en el juego es la parte más difícil de gestionar; por motivos de rendimiento, nuestra solución es la siguiente:

  • Uso del hash espacial como estructura de aceleración de búsquedas
  • Se realiza una estratificación previa de los elementos gráficos, teniendo en cuenta únicamente las colisiones entre partículas y triángulos de una capa a otra.
  • Para evitar que se produzcan atascos entre capas, solo se calculan las colisiones de las partículas con la dirección de la normal única de los triángulos.

El departamento de arte se encarga de dividir previamente el tejido en capas, y solo se tienen en cuenta las colisiones entre dichas capas. Se utiliza un hash como estructura de búsqueda acelerada y, para evitar que los objetos se queden atascados entre las capas, solo se tienen en cuenta las colisiones en una única dirección normal; si ya se ha producido una penetración, se omite y se deja que los pasos posteriores la corrijan.

En la práctica, utilizamos la posición de las partículas del subpaso anterior para realizar la colisión con la posición actual de las partículas, lo que permite desacoplar los datos de forma sencilla y evitar dependencias.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Separación por penetración entre capas

En cuanto a las partes en las que la colisión entre capas ya ha provocado una penetración, nos hemos basado en el método de «untangling cloth» y hemos utilizado una solución ligera que, mediante la estratificación de la tela y partiendo de los puntos de fijación de la misma, calcula los puntos de intersección entre los bordes y los triángulos de los distintos niveles; dado que la estructura de nuestros activos es necesariamente una malla uniforme,por lo que, a partir de los puntos de intersección de la malla, se pueden deducir con relativa facilidad los triángulos a los que pertenecen las demás partículas. Por último, se aplica una restricción de resorte a los pares partícula-triángulo que se solapan para resolver la penetración. En la práctica, debido a los subpasos, la probabilidad de penetración es relativamente baja, por lo que adoptamos una ejecución por fotogramas y bloques para aliviar la carga sobre el rendimiento.

2. Control de actuaciones en tiempo real

La mayor parte de los efectos físicos de la trama de «Love and Deep Space» se basan en el control y el ajuste de diversos efectos físicos a través de las escenas cinemáticas. Nuestros compañeros del departamento de herramientas han desarrollado y mantienen un conjunto de herramientas para escenas cinemáticas muy potentes y, partiendo de ellas, hemos creado varias pistas funcionales para regular de forma específica los efectos físicos.

Aquí tenemos un ejemplo de la pista de física de las escenas cinemáticas de nuestro juego «Dongka». Dado que nuestros compañeros de arte son muy exigentes con la representación visual, se puede observar que la configuración de toda la pista de física es bastante compleja.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

△Ejemplo de pista de física para escenas cinemáticas

SmoothBlendPose Track

Durante la interpretación, un problema muy habitual es el temblor físico provocado por los cambios bruscos de movimiento, algo que suele producirse tanto en las escenas narrativas como en los cambios de vestuario.

Hemos desarrollado un método bastante versátil que, al registrar la postura física inicial, realiza un cálculo de interpolación entre la postura inicial y la actual durante el cambio; de este modo, se reduce considerablemente el parpadeo,por supuesto, esto conlleva una cierta pérdida de tiempo, que suele rondar las decenas de milisegundos, lo cual resulta aceptable en la mayoría de los casos; además, se proporcionan algunos parámetros, como el número de interpolaciones y el tamaño del paso de interpolación, para que los artistas puedan ajustarlos según las necesidades reales.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Seguimiento de poses

Por supuesto, SmoothBlendPose tiene sus limitaciones y no puede garantizar una transición totalmente fluida, especialmente en los cortes de plano de algunas escenas complejas de la trama.También ofrecemos una solución más directa: guardar directamente «offline» el estado físico de un fotograma concreto y, durante la reproducción, aplicar ese estado físico guardado directamente a la tela, lo que permite evitar por completo los problemas derivados de los cambios de plano.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Editar pista de parámetros

Es muy difícil que un único activo físico pueda adaptarse a las diferentes situaciones que se dan en la trama; por ejemplo, a veces se quiere que la tela sea más blanda o más rígida, o que la amortiguación sea mayor o menor.Ofrecemos una pista de edición de parámetros que permite modificar los parámetros en tiempo real; la gran mayoría de los parámetros se pueden sobrescribir, lo que facilita mucho la realización de ajustes en un intervalo de fotogramas concreto. Esta modificación de parámetros también se puede utilizar para crear efectos especiales, como el efecto de rotura que se consigue en las animaciones mediante la edición de los parámetros de restricción.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Sección de animación

Los efectos físicos por sí solos no bastan para sustentar todos los aspectos de la representación de la escena; en muchas ocasiones, es necesario combinar la animación con la física para crear interacciones. Utilizamos las pistas de animación para lograr la conexión y la fusión entre la animación y la física, controlando con precisión el comportamiento en diferentes intervalos de fotogramas.En el proceso de producción real, existe una diferencia considerable entre el comportamiento de los movimientos en el DCC y su representación final en el motor; por ejemplo, tras modificar el sistema de rigs en tiempo real del motor, es posible que la animación se solape con otros elementos. Por ello, partiendo de la integración de la animación, podemos superponer efectos de colisión física para evitar dichos solapamientos.

La imagen animada muestra los efectos de interacción entre la física y la animación del collar, incluyendo el cambio de estado de la física a la animación, así como la transición entre diferentes animaciones.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Pista de colisiones y pista de viento

Collider Track y Wind Track permiten crear y eliminar dinámicamente cuerpos de colisión y campos de viento en las escenas cinemáticas. El estado de los cuerpos de colisión y los campos de viento se puede modificar de forma flexible según las necesidades de cada plano. Mediante la agrupación por personajes, tipos de componentes y capas de tela, se puede controlar con detalle el alcance de los objetos sobre los que se quiere ejercer influencia.

Además, se puede añadir control mediante fotogramas de animación a la gran mayoría de los parámetros de los cuerpos de colisión y las trayectorias del campo de viento, incluyendo la forma y el tamaño de los cuerpos de colisión, así como la dirección, el alcance, la intensidad y la turbulencia del campo de viento, lo que facilita a los artistas el control de los efectos físicos y permite regular con precisión los cambios.

En la animación se muestran algunos ejemplos de cómo se representan la cápsula orbital y el campo de viento.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

3. Desarrollo basado en Unity DOTS

Empleos + Ráfagas + Matemáticas

DOTS es un conjunto de herramientas muy potente que permite desarrollar aplicaciones multihilo de alto rendimiento directamente en el nivel de C#. Nuestro sistema físico está construido íntegramente en C# utilizando DOTS, lo que facilita enormemente las iteraciones de funcionalidades y la depuración. En la actualidad, podemos simular más de 2.000 partículas esqueléticas.

Por supuesto, también hemos llevado a cabo algunas optimizaciones específicas en el proyecto para mejorar aún más el rendimiento.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Ocultar trabajo

Dado que el número de trabajos y sus dependencias están fijos en la simulación, y que los datos de los trabajos no cambian con frecuencia, dentro de cada fotograma suele ejecutarse en bucle un grupo de trabajos con el mismo número y las mismas dependencias. Unity Jobs necesita volver a crear cada trabajo cada vez que se inicia una tarea; aunque esto se puede mitigar iniciando las tareas con antelación, sigue bloqueando el hilo principal. Además, una vez finalizada la ejecución, es necesario borrar el trabajo.A partir de estas observaciones, hemos desarrollado una solución de «Cache Job» que consiste en crear previamente los datos de los trabajos y reutilizarlos en cada ejecución, evitando así la pérdida de rendimiento que supone volver a crear un trabajo cada vez.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

En la práctica, es bastante sencillo, ya que se trata de una estructura específica que solo tiene en cuenta algunos casos de uso fijos. Se ha añadido una cola atómica (Atomic Queue) para almacenar los trabajos de caché, utilizando la técnica «fetch and add array» para guardar los trabajos concretos. A la derecha se muestra un diagrama esquemático del proceso de ejecución de los trabajos de caché por parte de los trabajadores.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Características intrínsecas del neón

Burst genera código SIMD de alto rendimiento para diferentes plataformas, que se puede consultar fácilmente en Burst Inspector. Tras revisar Burst Inspector y realizar pruebas en dispositivos reales, en algunos casos también es posible mejorar aún más el rendimiento escribiendo manualmente funciones intrínsecas de ARM Neon.

A continuación se muestra un ejemplo de cómo implementar una función para comprobar si un vector contiene algún elemento mayor que 0.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!
¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Dot(float4)

En cuanto a la multiplicación escalar, aquí enumero tres métodos; el uso de las funciones intrínsecas NEON permite obtener una mejora de rendimiento de aproximadamente un 30 % en comparación con las funciones matemáticas en los casos de prueba.Si el modelo de dispositivo es compatible con ARMv8.2, se pueden utilizar las nuevas instrucciones de suma por reducción para mejorar aún más el rendimiento. En general, la mayoría de los modelos más populares actualmente en el mercado son compatibles con ARMv8.2.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Transpose(float4x4)

En lo que respecta a los cálculos de transposición, se puede observar que el código ensamblador generado por Mathematics parece tener un rendimiento muy bajo; sin embargo, al escribir manualmente las funciones intrínsecas de NEON, se consigue una mejora enorme del rendimiento.

Si solo se trata de una transposición pura y simple, se puede utilizar directamente la lectura entrelazada; en este caso, se ha implementado así porque, en mi uso habitual, convierto el producto escalar en un producto vectorial mediante la transposición de cuatro float4.

Aquí solo se ofrecen ejemplos de los más habituales en estos dos proyectos. Dado que el código de «mathematics» suele estar incrustado, a la hora de optimizarlo habrá que tener en cuenta el contexto del código; para ello, se pueden combinar el «Burst Inspector» y las pruebas en dispositivos reales con el fin de realizar pruebas de rendimiento específicas.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

4. Módulo de detección de colisiones

¿Por qué volver a desarrollar el módulo de física desde cero en lugar de utilizar el módulo ya consolidado de Unity?

Unity cuenta por sí mismo con un conjunto de módulos de física maduros basados en PhysX, y la decisión de desarrollar uno nuevo al margen de los módulos de física maduros de Unity se basa principalmente en las siguientes consideraciones:

  • «Love and Deep Space» cuenta con una gran variedad de modos de juego, y la configuración de las capas entre ellos es relativamente independiente, por lo que nos gustaría mucho poder mantener un conjunto de ajustes de capas para cada uno de ellos.
  • Algunos módulos, como el de combate, requieren mecanismos especiales de activación y salida que nos gustaría que fueran compatibles desde el nivel más básico; además, nos gustaría disponer de un control más flexible sobre el flujo de ejecución.
  • Por último, también tenemos algunas ideas sobre la optimización del rendimiento: ¿sería posible mejorar el rendimiento utilizando DOTS en los casos en los que solo se necesita la detección de colisiones?
¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Las implementaciones en «Love and Deep Space» incluyen:

  • Se han implementado prácticamente todas las funciones nativas de consulta de colisiones.
  • Lógica personalizada de «Update» y «Trigger»
  • Interfaz de consulta segura para subprocesos, que las capas superiores pueden invocar sin problemas
  • Gracias a la estructura ligera de DOTS, en las pruebas de rendimiento se puede alcanzar una mejora de hasta un 15 % aproximadamente.

Ejemplo de proceso de consulta

Dado que, en el dispositivo real, el número de subprocesos es fijo (4), el asignador de memoria puede realizar la asignación de antemano en función del número de subprocesos, de modo que, en el momento de la asignación, se pueda acceder directamente a la memoria según el índice del subproceso actual.

Utilizando un BVH dinámico basado en SAH como estructura de aceleración de la fase amplia, se realiza un equilibrio de rotación en los nodos de varios niveles adyacentes al nodo de la operación actual durante las operaciones de inserción, eliminación y desplazamiento fuera de rango.

Dado que los objetivos funcionales de la detección de colisiones son relativamente generales y los requisitos de precisión no son tan elevados, hemos sacrificado un poco de precisión y hemos simplificado algunos algoritmos de detección de colisiones para mejorar el rendimiento.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Ejemplo de proceso de activación

Para satisfacer las necesidades del módulo de combate, hemos diseñado una lógica de activación especial: las entradas y salidas (Enter y Exit) de los triggers deben aparecer por pares. Como se puede observar en el siguiente diagrama de flujo, al eliminar «b» de la función en la que «a» activa «b», se activan todos los colisionadores que se solapan con «b».esto difiere de la lógica nativa de Unity: en Unity nativo, eliminar «b» de la lógica del «trigger» no activa los «triggers» de otros colisionadores. Por último, utilizamos el contador «History» para marcar la versión del colisionador, lo que resuelve algunos problemas potenciales que podría acarrear la lógica de reutilización.

¡Resumen de la primera sesión de intercambio técnico en profundidad sobre «Love and Deep Space»!

Te damos la bienvenida a nuestro equipo,

¡Creemos juntos un futuro mejor gracias a la tecnología!

原创文章,作者:游茶妹儿,禁止转载:https://youxichaguan.com/es/archives/213130

Like (0)
游茶妹儿
Cuando JJ Lin se cruzó con «Honor of Kings», el sector del turismo y la cultura de todo el país se puso en alerta
Previous 15 hours ago
Apostando por el «no consenso», con más de diez millones de usuarios: así se puede jugar con la combinación de «IA + videojuegos»
Next 15 hours ago

相关推荐