El marco de generación procedimental de contenido es un conjunto de herramientas con el que podrás crear tus propios contenidos y herramientas procedimentales en Unreal Engine. El procesamiento de PCG en la GPU permite a los artistas técnicos y a los diseñadores enviar muchas tareas de procesamiento de generación procedimental de contenido directamente a la GPU para liberar recursos de la CPU.
El procesamiento de generación procedimental de contenido en la GPU es eficaz para diversas tareas, como el procesamiento de puntos, la generación en tiempo de ejecución y la generación de mallas estáticas.
En la actualidad, el procesamiento de la GPU está disponible en una pequeña cantidad de nodos, entre otros Copiar puntos y Generador de mallas estáticas, así como un nuevo nodo HLS Personalizado que se puede programar en lenguaje HLSL.
Próximamente habrá más nodos disponibles que permitan la ejecución en GPU.
Los nodos que están establecidos para dirigirse a la GPU se etiquetan con GPU en el grafo de generación procedimental de contenido. Un subconjunto de nodos de GPU conectados se ejecuta de forma conjunta en la GPU de forma eficiente y se llama grafo computacional.
Número | Descripción |
1 | Transfiriendo datos entre la CPU y la GPU. Estos puntos representan un coste de rendimiento. |
2 | Nodos de ejecución de GPU que se ejecutan juntos. |
Enviar a la GPU puede aumentar el rendimiento con respecto a la ejecución en la CPU cuando hay suficientes puntos en los datos para aprovechar al máximo el hardware de la GPU. Además, una secuencia de nodos de GPU conectados con un nodo Generador de mallas estáticas habilitado para GPU ofrece una vía rápida para la generación de mallas estáticas.
Es importante tener en cuenta que hay un coste de CPU por transferir datos entre la CPU y la GPU, así como por preparar un grafo computacional para su ejecución. Por lo tanto, la forma óptima de utilizar la función de ejecución en la GPU es agrupar los nodos habilitados para GPU y minimizar la cantidad de datos que se transfieren dentro y fuera de cada grafo computacional.
Nodos compatibles
Nodo HLS Personalizado
El nodo HLS Personalizado se puede usar para programar tareas de procesamiento de datos arbitrarias a través del código fuente HLSL creado por el usuario. El código fuente se inyecta en un sombreador computacional y se ejecuta sobre los elementos de datos en paralelo en el hardware de la GPU.
Este nodo proporciona acceso de bajo nivel al hardware de la GPU y está disponible para usuarios avanzados.
Opción
| Descripción |
Tipo de núcleo | Selecciona un preajuste para el comportamiento del nodo. Las opciones disponibles están documentadas en la sección Tipos de núcleo del nodo HLSL Personalizado más adelante. |
Pines de entrada | Define los datos que se toman como entrada. Al abrir el menú desplegable, se ofrecen las siguientes opciones:
|
Pines de salida | Define los datos que salen desde el nodo. Tiene las mismas opciones que los pines de entrada, pero incluye opciones adicionales para ajustar los datos de salida. Se tratan en la sección Configuración de pines más adelante. |
Anulación de fuente del núcleo | Se utilizar para reemplazar el campo Fuente de sombreador por un recurso UComputeSource. |
Fuentes adicionales | Permite referencias a recursos de UComputeSource adicionales para que se empaqueten con tu nodo HLSL Personalizado. |
Silenciar errores de datos de pines sin escribir | Silencia las advertencias en los pines de salida con datos potencialmente no inicializados. |
Valor de inicialización | Define el valor de inicialización utilizado para controlar la generación aleatoria. |
Volcar HLSL cooked | Imprime los datos HLSL cooked en el registro cuando se genera para la depuración. |
Volcar descripciones de datos | Imprime las descripciones de los datos de entrada y salida en el registro cuando se genera para la depuración. |
Imprimir valores de depuración del sombreador | Proporciona un registro de depuración sencillo desde el código del sombreador. Este tema se trata en Depuración de HLSL Personalizado más adelante. |
Editor de código fuente HLSL
El editor de código fuente de HLSL permite una creación rápida de nodos HLSL Personalizado. Se puede encontrar en el editor de gráficos de generación procedimental de contenido, en Ventana > Fuente de HLSL, o seleccionando un nodo HLSL Personalizado y haciendo clic en el botón de abrir el editor de fuente en los ajustes del nodo.
El editor de código fuente de HLSL se compone de tres partes:
Panel Declaraciones
Funciones del sombreador
Fuente de sombreador
El panel de declaraciones sirve como referencia de la API para escribir código de sombreador. Las declaraciones se generan automáticamente a partir de los ajustes del nodo HLSL Personalizado, como sucede con el tipo de núcleo y los pines de entrada y salida.
El campo Funciones del sombreador permite a los autores crear funciones reutilizables para llamar en su fuente de sombreador.
El campo Fuente de sombreador es donde se implementa el punto de entrada principal para la implementación del núcleo.
Tipos de núcleo del nodo HLSL Personalizado
El tipo de núcleo define una configuración preestablecida para el comportamiento del nodo.
Point Porcessor
El tipo de núcleo Point Processor es ideal para modificar puntos. Requiere que el pin de entrada y de salida principales sean de tipo Point, y ejecuta el código HLSL una vez por cada punto. Los datos enviados por el pin de salida principal tienen la misma disposición que el pin de entrada principal, lo que significa que la cantidad de datos y la cantidad de elementos son idénticas.
Todos los puntos de la salida principal se inicializan automáticamente a partir de la entrada principal, por lo que solo es necesario establecer los atributos de salida que deban cambiarse.
También puedes usar el procesador de puntos para crear pines de entrada y salida adicionales que deberás configurar manualmente para establecer el tipo de datos y el recuento de datos/elementos deseados.
Point Generator
El tipo de núcleo Point Generator es ideal para crear y rellenar un conjunto de puntos. Requiere que el pin de salida principal sea de tipo Point y ejecuta el código HLSL una vez para cada punto.
Este tipo de núcleo cuenta con las siguientes opciones adicionales:
Opción
| Descripción |
Número de elementos
| Determina el número de puntos que se generan. El código de sombreador se ejecuta en cada punto generado. |
Al igual que con el procesador de puntos, puedes usar el generador de puntos para crear pines de entrada y salida adicionales que debes configurar manualmente para establecer el tipo de datos y el recuento de datos o elementos deseados.
Personalizar
El tipo de núcleo Custom expone un control detallado sobre la ejecución para casos de uso avanzados. A diferencia de los otros dos tipos de núcleo, no se aplican ajustes forzosos en los pines de entrada o salida, ya que el nodo no da por sentada ninguna relación específica entre la entrada y la salida. Los datos de salida deben configurarse en los ajustes de pines de salida, tal y como se explica a continuación. También hay que configurar el número de subprocesos que debe ejecutar el código de sombreador.
Este tipo de núcleo cuenta con las siguientes opciones adicionales:
Opción | Descripción |
Enviar recuento de subprocesos | Determina el número de subprocesos que utiliza el código de sombreador para ejecutarse. Están disponibles los siguientes modos:
|
Configuración de pines
Cualquier pin no controlado por el tipo de núcleo debe configurarse manualmente.
Para los pines de salida, es necesario describir explícitamente el tamaño y la disposición de los datos, los cuales se pueden establecer en el menú desplegable GPU Properties dentro de los ajustes del pin de salida.
Modo de inicialización | Describe cómo se inicializarán los datos de salida para este pin. El menú contiene los siguientes modos:
|
Pins to Initialize From | Define los pines de entrada desde los que inicializar los datos de este pin. |
Data Count Mode | Define el número de objetos de datos. El menú contiene los siguientes modos:
|
Multiplicidad de datos | Combina recuentos de datos si hay varios pines desde los que inicializar. Modos disponibles:
|
Modo de recuento de elementos | Define el número de elementos. El menú contiene los siguientes modos:
|
Multiplicidad de elementos | Combina recuentos de elementos si hay varios pines desde los que inicializar. Modos disponibles:
|
Attribute Inheritance Mode | Define cómo heredar nombres, tipos y valores de atributo. El menú contiene los siguientes modos:
|
Attributes to Create | Define una lista de nuevos atributos que crear en los datos de salida. |
Depuración de HLSL Personalizado
Las funciones de visualización de depuración (tecla de acceso rápido por defecto 'D') e inspección (tecla de acceso rápido por defecto 'A') funcionan para nodos GPU y permiten la inspección de los datos que fluyen a través de los nodos de GPU.
También es posible depurar código de sombreador personalizado activando la opción Imprimir valores de depuración de sombreador del nodo HLSL Personalizado. Esto expone una nueva función WriteDebugValue, que puedes utilizar para escribir valores float en un búfer que se registra durante la ejecución. El tamaño del búfer se controla mediante la propiedad de tamaño de búfer de depuración.
Ejemplos
Ejemplo 1: compensación de altura con una onda sinusoidal
En el ejemplo de abajo, se utiliza un procesador de puntos para aplicar una compensación de altura basada en una onda sinusoidal a un conjunto de puntos.
Se ha añadido el siguiente código al campo Fuente de sombreador de la ventana del editor de fuente HLSL:
// Get the position of the incoming point from input pin ‘In’.
float3 Position = In_GetPosition(In_DataIndex, ElementIndex);
// Compute a sine wave with amplitude 500cm and wavelength 400cm.
const float Wave = 500.0f * sin(Position.x / 400.0f);
// Add the wave to the Z coordinate of the point’s position.
Position.z += Wave;
// Write the offset position to the output point on pin ‘Out’.
Ejemplo 2: cómo crear un atributo
En el ejemplo siguiente, se utiliza un generador de puntos para crear una cuadrícula de puntos, y se utiliza un atributo para controlar la altura de la cuadrícula.
Se ha añadido el siguiente código al campo Fuente de sombreador de la ventana del editor de fuente HLSL:
// Get PCG Component bounds.
const float3 BoundsMin = GetComponentBoundsMin();
const float3 BoundsMax = GetComponentBoundsMax();
// Get the current point position in a 2D grid, based on the
// number of points and the component bounds.
float3 Position = CreateGrid2D(ElementIndex, NumPoints, BoundsMin, BoundsMax);
Position.z += InHeight_GetFloat(0, 0, 'GridHeight');
// Set the point's position.
Se puede acceder a los atributos en el código de sombreador utilizando las funciones de obtención y establecimiento proporcionadas y consultando el atributo por su nombre entre apóstrofes. Por ejemplo, ‘GridHeight’.
Ejemplo 3: generación de mallas aleatorias en un paisaje
El nodo HLSL Personalizado también puede ejecutar una secuencia de operaciones.
En el siguiente ejemplo, el código de sombreador realiza las siguientes operaciones:
Crea varios puntos en un paisaje.
Aplica un ajuste de posición aleatorio a cada punto.
Establece la posición del punto.
Escribe un valor de inicialización aleatorio en cada punto
Lee un conjunto de atributos que contiene una lista de mallas estáticas y asigna una malla aleatoria a cada punto.
Después del nodo Custom HLSL, hay un generador de malla estática con la ejecución en la GPU activa y el atributo de malla establecido en 'MeshPath'.
En la GPU, los atributos de tipo cadena, nombre, ruta de objeto virtual y ruta de clase virtual se convierten en StringKeys, que identifican de forma única cada cadena.
El siguiente código se añade al campo Fuente de sombreador de la ventana fuente de HLSL:
// Get generation volume bounds
const float3 BoundsMin = GetComponentBoundsMin();
const float3 BoundsMax = GetComponentBoundsMax();
// Compute a position on a 2D grid within the volume.
float3 Pos = CreateGrid2D(ElementIndex, NumPoints, BoundsMin, BoundsMax);
// Initialize the random seed from the position.
uint Seed = ComputeSeedFromPosition(Pos);
Nodo generador de mallas estáticas
Puedes hacer que el nodo generador de mallas estáticas se ejecute en la GPU activando la opción Ejecutar en GPU en los ajustes del nodo.
Instancias procedimentales
Cuando se configura el generador de mallas estáticas para que se ejecute en la GPU, configura instancias de malla íntegramente en la GPU, lo que ahorra tiempo y memoria a la CPU. Esto utiliza el componente de mallas estáticas con instancias procedimentales. Esta puede ser una ruta muy eficiente para generar mallas, pero es experimental y conlleva los siguientes inconvenientes:
Las instancias no se conservan ni se guardan de ninguna manera. Solo existen en tiempo de ejecución en la memoria de la GPU.
Por lo tanto, el caso de uso principal es la generación en tiempo de ejecución.
La iluminación baked estática y el nivel de detalle alto requieren información persistente de la instancia y tampoco son compatibles en este momento.
Actualmente, varias funciones requieren acceso de la CPU a los datos de instancia y no son compatibles:
Colisiones/Física
Navegación
Trazado de rayos
Afectar a la iluminación de campo de distancia
La implementación en la GPU es experimental y no todas las funciones del generador de mallas estáticas son compatibles.
Tipos de selector de malla
Los siguientes selectores de malla son compatibles al ejecutar en la GPU y, debido a cómo se asignan las instancias, existen ligeras diferencias de comportamiento.
Ponderado (PCGMeshSelectorWeighted)
Al igual que la implementación en la CPU, este modo utiliza los valores de inicialización aleatorios del punto de entrada y los pesos de selección configurados para seleccionar aleatoriamente la malla para cada instancia. Estas mallas deben establecerse en el nodo en lugar de estar controladas por atributos.
El sistema usa ponderaciones para determinar cuántas instancias deben asignarse a cada malla. Se realiza una sobreasignación basada en una heurística para minimizar la posibilidad de saturar la asignación de uno o más elementos primarios y perder instancias.
Este modo se basa en que el atributo de valor de inicialización de los puntos esté bien inicializado, por ejemplo, utilizando la función de sombreador `ComputeSeedFromPosition()`. Si todos los valores de inicialización de puntos se establecen en el mismo valor, se hará la misma selección para todos los puntos y es posible que se supere la asignación estimada, lo que provocará que falten instancias en el resultado.
Por atributos (PCGMeshSelectorByAttribute)
Otros tipos de selectores de malla (por ejemplo, PCGMeshSelectorWeightedByCategory) ahora mismo no son compatibles al ejecutarse en la GPU.
El recuento final de instancias asignadas se puede inspeccionar seleccionando los componentes de malla estática instanciados procedimentalmente generados y comprobando la propiedad de número de instancias.
Empaquetado de datos de instancia
De forma similar a la ejecución en la CPU, los atributos se pueden empaquetar en datos de instancia.
Antes de la ejecución en la GPU, el sistema necesita saber cuántos atributos se empaquetarán y, por lo tanto, solo es compatible con el tipo de empaquetador por atributos (PCGInstanceDataPackerByAttribute).
Otros nodos
Por el momento, los siguientes nodos son compatibles con la ejecución en la GPU:
Copiar puntos
Partición de atributos
Por el momento, solo es compatible con la creación de particiones en atributos de tipo cadena, ruta de objeto temporal o ruta de clase temporal.
De normal a densidad
Recuento de datos
Generador de mallas estáticas
HLSL Personalizado
No es compatible con la ejecución en la CPU.
Fuentes de cálculo
Los recursos de fuente de cálculo facilitan el intercambio de código fuente y reducen la duplicación de código entre nodos.
Los recursos son compatibles con la edición en línea de código fuente de HLSL con resaltado de sintaxis y sintaxis específica de generación procedimental de contenido, como etiquetas de datos y nombres de atributos.
Los recursos de fuentes de cálculo también pueden hacer referencia a otros recursos de fuentes de cálculo mediante la propiedad de fuentes adicionales, que crea jerarquías de dependencia entre varias fuentes de cálculo.
Etiquetas de datos
Las etiquetas de datos pueden usarse para referenciar datos por etiqueta en lugar de por índice en la fuente de HLSL personalizado. Las etiquetas de los datos se comunican en los datos a través de etiquetas con el prefijo PCG_DATA_LABEL.
Algunos nodos etiquetan automáticamente sus datos de salida, por ejemplo:
Obtener datos de textura
Obtener datos de textura virtual
Generar mapas de hierba
Generación de mapas de hierba
La generación procedimental de contenido es compatible con muestrear capas de hierba de paisaje a partir de un material de paisaje específico para facilitar los procesos de trabajo de generación procedimental en tiempo de ejecución.
Configura tu material de paisaje con un nodo de salida de hierba de paisaje. Para obtener más información sobre cómo configurar un material de paisaje, consulta Materiales del paisaje.
Conecta tus datos de paisaje a un nodo Generar mapas de hierba. Selecciona los tipos de hierba que quieras directamente mediante la anulación o la exclusión.
Muestrea las texturas de tu mapa de hierba. Puedes muestrear por índice o por las etiquetas de datos asignadas automáticamente. Se ha añadido el siguiente código al campo Fuente de sombreador de la ventana del editor de fuente HLSL:
float3 Min = GetComponentBoundsMin();
float3 Max = GetComponentBoundsMax();
float3 Position = CreateGrid2D(ElementIndex, NumPoints, Min, Max);
uint Seed = ComputeSeedFromPosition(Position);
Position.xy += (float2(FRand(Seed), FRand(Seed)) - 0.5) * 45.0;
Position.z = LS_GetHeight(Position);
float Density = FRand(Seed);
float Thresh = GrassMaps_SampleWorldPos('GSM_PCGGrass1', Position.xy).x;
Si tienes pensado muestrear las texturas de tu mapa de hierba solo en la GPU, puedes cambiar Omitir lectura en la CPU en el grafo de generación procedimental de contenido para mejorar significativamente el rendimiento.
Capas de paisaje pintadas:
Generación resultante:
Cómo utilizar texturas virtuales en generación procedimental de contenido
La generación procedimental de contenido es compatible con el uso de texturas virtuales como parte del proceso de trabajo de generación de contenido procedimental.
Muestreo de texturas virtuales
Es posible muestrear texturas virtuales a partir de datos de paisaje para mejorar el rendimiento del muestreo de altura.
Ejemplo 1: datos de paisaje
Para muestrear datos del paisaje con texturas virtuales, asegúrate de que la opción Muestrear texturas virtuales esté activado en los ajustes del nodo Get Landscape Data. Permite al nodo Landscape Data usar cualquier textura virtual proporcionada por el material del paisaje correspondiente.
Solo afecta al muestreo de la GPU.
// Get Position and Height
float3 Position = CreateGrid2D(ElementIndex, NumPoints, GetComponentBoundsMin(), GetComponentBoundsMax());
Position.z = A_GetHeight(Position);
// Get Normal and Orientation
const float3 Normal = A_GetNormal(Position);
const FQuat Orientation = QuatFromNormal(Normal);
// Get Base Color
const float3 BaseColor = A_GetBaseColor(Position);
Ejemplo 2: datos de textura virtual
Para muestrear una textura virtual, consulta el mundo en busca de componentes de textura virtuales en tiempo de ejecución. Cada uno producirá unos datos de textura virtual, etiquetados con una etiqueta de datos para el recurso de textura virtual en tiempo de ejecución.
float3 Position = CreateGrid2D(ElementIndex, NumPoints, GetComponentBoundsMin(), GetComponentBoundsMax());
// Sample virtual textures
bool bInsideVolume;
float3 BaseColor;
float Specular;
float Roughness;
float WorldHeight;
float3 Normal;
float Displacement;
Precargado de texturas virtuales
Es importante asegurarse de que las texturas virtuales se hayan precargado antes de generarlas, ya que, de lo contrario, los resultados de las muestras podrían no ser precisos.
Para solicitar el precargado de texturas virtuales, añade un parámetro de grafo de tipo FPCGVirtualTexturePrimingInfo a tu grafo de generación procedimental de contenido. Esto expone las siguientes opciones:
Textura virtual | Define la textura virtual que se va a preparar. |
Cuadrícula | Define la cuadrícula más grande en la que se muestrea la textura virtual en el grafo. Las texturas virtuales están preparadas para el radio de generación dictado por esta cuadrícula. |
Tamaño del téxel del mundo | Define el tamaño deseado de un téxel en la textura virtual precargada. Esto determinará qué nivel de mapa MIP debe prepararse. |
Puedes controlar la generación procedimental de contenido del precargado de las texturas virtuales con el comando de consola pcg.VirtualTexturePriming.Enable. Puedes depurar esta función con el comando pcg.VirtualTexturePriming.DebugDrawTexturePrimingBounds.