O Framework de Geração Procedural de Conteúdo (PCG) é um conjunto de ferramentas para criar seu próprio conteúdo e ferramentas procedurais dentro da Unreal Engine. O processamento de GPU de PCG permite que designers e artistas técnicos enviem muitas tarefas de processamento de PCG diretamente para a GPU para liberar recursos na CPU.
O processamento por GPU com PCG é eficiente para diversas tarefas, como processamento de pontos, geração de tempo de execução e surgimento de malha estática.
O processamento por GPU está disponível em poucos nós, incluindo Pontos de cópia e Gerador de malha estática, bem como um novo nó HLSL personalizado adicionado, que pode ser programado usando a linguagem HLSL.
No futuro, outros nós compatíveis com a execução por GPU estarão disponíveis.
Os nós definidos para a GPU são rotulados com GPU no gráfico de PCG. Um subconjunto de nós de GPU conectados é executado em conjunto na GPU de maneira eficiente e é chamado de Gráfico de computação.
Number | Descrição |
1 | Transferência de dados entre a CPU e a GPU. Esses pontos representam um custo de desempenho. |
2 | Nós de execução de GPU que são executados em conjunto. |
O direcionamento da GPU pode aumentar o desempenho em relação à execução da CPU quando há pontos suficientes nos dados para utilizar totalmente o hardware da GPU. Além disso, uma sequência de nós de GPU conectados com um nó Gerador de malha estática habilitado para GPU fornece um caminho rápido para o surgimento de malhas estáticas.
É importante observar que há um custo de CPU para transferir dados entre a CPU e a GPU e para preparar um gráfico de computação para execução. Portanto, a maneira ideal de usar o recurso de execução de GPU é agrupar os nós habilitados para GPU e minimizar a quantidade de dados transferidos para e de cada gráfico de computação.
Nós compatíveis
Nó HLSL personalizado
O nó HLSL personalizado pode ser usado para tarefas arbitrárias de processamento de dados a serem codificadas por código-fonte HLSL criado pelo usuário. O código-fonte é injetado em um shader de computação e executado sobre elementos de dados em paralelo no hardware da GPU.
Esse nó fornece acesso de baixo nível ao hardware da GPU e está disponível para usuários avançados.
Opção
| Descrição |
Tipo de kernel | Seleciona uma predefinição para o comportamento do nó. As opções disponíveis estão documentadas na seção Tipos de kernel do nó HLSL personalizado abaixo. |
Pins de entrada | Define os dados usados como entrada. A abertura do menu desdobrável oferece as seguintes opções:
|
Pins de saída | Define os dados que são enviados a partir do nó. Contém as mesmas opções dos pins de entrada, com opções adicionais para configurar os dados de saída. Estas são abordadas na seção Configuração de pins abaixo. |
Substituição da fonte do kernel | Usada para substituir o campo Fonte do shader por um ativo UComputeSource. |
Fontes adicionais | Permite fazer referência a ativos UComputeSource adicionais para serem empacotados com o nó HLSL personalizado. |
Silenciar erros de dados de pins não gravados | Silencia avisos em pins de saída com dados possivelmente não inicializados. |
Propagação | Define o valor inicial usado para direcionar a geração aleatória. |
Descartar HLSL preparado | Imprime os dados HLSL preparados no log quando eles são gerados para depuração. |
Descartar descrições de dados | Imprime as descrições dos dados de entrada e saída no log quando estes são gerados para depuração. |
Valores de depuração do shader de impressão | Fornece um registro de depuração simples a partir do código do shader. Abordado em Depurar o HLSL personalizado abaixo. |
Editor de código-fonte do HLSL
O Editor de código-fonte do HLSL permite a criação rápida de nós HLSL personalizados. Ele pode ser encontrado no Editor gráfico de PCG em Janela -> Editor de código-fonte do HLSL ou selecionando um nó HLSL personalizado e clicando no botão Editor de código-aberto nas configurações do nó.
O Editor de código-fonte do HLSL está dividido em três partes:
Painel Declarações
Funções de shader
Fonte do shader
O painel Declarações serve como uma referência de API para escrever o código do shader. As declarações são geradas de forma automática pelas configurações do nó HLSL personalizado, como o tipo de kernel e as configurações de pin de entrada/saída.
O campo Funções de shader permite que os autores criem funções reutilizáveis para chamada na Fonte do shader.
O campo Fonte do shader é onde você implementa o ponto de entrada principal para a implementação do kernel.
Tipos de kernel do nó HLSL personalizado
OTipo de kernel define uma predefinição para o comportamento do nó.
Processador de pontos
O tipo de kernel Processador de pontos é ideal para modificar pontos. Ele exige que os pins primários de entrada e saída sejam do tipo Ponto e executa o código HLSL uma vez para cada ponto. Os dados enviados pelo pin primário de saída têm o mesmo layout do pin primário de entrada , ou seja, o número de dados e o número de elementos são idênticos.
Todos os pontos na saída primária são inicializados pela entrada principal. Portanto, basta definir os atributos de saída que devem ser alterados.
Você também pode usar o Processador de pontos para criar pins adicionais de entrada e saída que devem ser configurados manualmente para definir o tipo de dados desejado e a contagem de dados/elementos.
Gerador de pontos
O tipo de kernel Gerador de pontos é ideal para criar e preencher um conjunto de pontos. Ele exige que o pin de saída principal seja do tipo Ponto e executa o código HLSL uma vez para cada ponto.
Esse tipo de kernel tem as seguintes opções adicionais:
Opção
| Descrição |
Contagem de pontos
| Determina o número de pontos gerados. O código do shader é executado em cada ponto gerado. |
Semelhante ao Processador de pontos, é possível utilizar o Gerador de pontos para criar pins de entrada e saída adicionais, que devem ser configurados manualmente para definir o tipo de dados e a contagem de dados ou elementos desejados.
Personalizado
O tipo de kernel Personalizado oferece um refinamento de controle sobre a execução para casos de uso avançados. Ao contrário dos outros dois tipos de kernel, não há configurações aplicadas nos pins de entrada ou saída, pois o nó não assume nenhuma relação específica entre a entrada e a saída. Os dados de saída devem ser definidos nas configurações do pin de saída documentadas abaixo. O número de threads que devem executar o código do shader também deve ser configurado.
Esse tipo de kernel tem as seguintes opções adicionais:
Opção | Descrição |
Contagem de threads de despacho | Determina o número de threads que o código do shader usa para executar. Os modos a seguir estão disponíveis:
|
Configuração de pins
Os pins não acionados pelo Tipo de kernel devem ser configurados manualmente.
Para pins de saída, é necessário descrever explicitamente o tamanho e o layout dos dados, que podem ser definidos no menu suspenso Propriedades da GPU nas configurações do pin de saída.
Modo de inicialização | Descreve como os dados de saída para esse pin serão inicializados. O menu contém os seguintes modos:
|
Pins dos quais inicializar | Define os pins de entrada dos quais inicializar os dados desse pin. |
Modo de contagem de dados | Define o número de objetos de dados. O menu contém os seguintes modos:
|
Multiplicidade de dados | Combina contagens de dados se houver vários pins a partir dos quais inicializar. Modos disponíveis:
|
Modo de contagem de elementos | Define o número de elementos. O menu contém os seguintes modos:
|
Multiplicidade de elemento | Combina contagens de elementos se houver vários pins dos quais inicializar. Modos disponíveis:
|
Modo de herança de atributos | Define como herdar nomes, tipos e valores de atributos. O menu contém os seguintes modos:
|
Atributos para criar | Define uma lista de atributos para criar nos dados de saída. |
Depurar o HLSL personalizado
As funcionalidades Exibição de depuração (tecla de atalho padrão "D") e Inspeção (tecla de atalho padrão "A") funcionam para nós de GPU e permitem a inspeção dos dados que passam pelos nós de GPU.
Também é possível depurar o código do shader personalizado ativando a opção Imprimir valores de depuração do shader no nó HLSL personalizado. Isso expõe uma nova função WriteDebugValue, que pode ser usada para gravar valores float em um buffer que é registrado durante a execução. O tamanho do buffer é controlado pela propriedade Tamanho do buffer de depuração.
Exemplos
Exemplo 1: deslocamento da altura usando uma onda senoidal
No exemplo a seguir, um Processador de pontos é usado para aplicar um deslocamento de altura baseado em onda senoidal a um conjunto de pontos.
O seguinte código foi adicionado ao campo Fonte do shader da janela Editor de código-fonte do 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’.
Exemplo 2: Como criar um atributo
No exemplo a seguir, um Gerador de pontos é usado para criar uma grade de pontos e usa um conjunto de atributos para controlar a altura da grade.
O seguinte código foi adicionado ao campo Fonte do shader da janela Editor de código-fonte do 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.
Os atributos podem ser acessados no código do shader usando as funções Get e Set fornecidas e consultando o atributo por nome, delimitando-o entre apóstrofos. Por exemplo, ‘GridHeight’.
Exemplo 3: fazer surgir malhas aleatórias em uma paisagem
O nó HLSL personalizado também executa uma sequência de operações.
No exemplo abaixo, o código do shader realiza as seguintes operações:
Cria vários pontos em uma paisagem.
Aplica um ajuste de posição aleatório a cada ponto.
Define a posição do ponto
Grava um valor de propagação aleatória em cada ponto
Lê um conjunto de atributos que contém uma lista de malhas estáticas e atribui uma malha aleatória a cada ponto.
Abaixo do nó Custom HLSL, há um gerador de malha estática com execução de GPU habilitada e o atributo de malha definido como "MeshPath".
Na GPU, atributos do tipo String, Name, Soft Object Path e Soft Class Path se tornam StringKeys, que identificam cada string de maneira exclusiva.
O seguinte código foi adicionado ao campo Fonte do shader da janela de código-fonte do 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);
Nó Gerador de malha estática
Você pode fazer com que o nó Gerador de malha estática seja executado na GPU ativando a opção Executar na GPU nas configurações de nó.
Instanciação procedural
Quando o Gerador de malha estática é configurado para execução na GPU, ele configura instâncias de malha inteiramente na GPU, economizando tempo e memória da CPU. Ele usa o Componente de malha estática instanciado de maneira procedural. Esse pode ser um caminho muito eficiente para o surgimento de malhas, mas é experimental e inclui as seguintes desvantagens:
As instâncias não são salvas nem persistidas. Elas existem apenas durante a tempo de execução na memória da GPU.
Portanto, o principal caso de uso é a Geração em tempo de execução.
A iluminação incorporada estática e o HLOD exigem informações de instância persistentes e também não são compatíveis no momento.
No momento, várias funcionalidades exigem acesso da CPU aos dados de instância e não são compatíveis:
Colisões/física
Navegação
Traçado de raios
Afetar a iluminação do campo de distância
A implementação em GPU é experimental e nem todas as funcionalidades do Gerador de malha estática são compatíveis.
Tipos de seletores de malha
Os seletores de malha a seguir são compatíveis com a execução na GPU e, devido ao modo de alocação das instâncias, há pequenas diferenças de comportamento.
Ponderado (PCGMeshSelectorWeighted)
De modo semelhante à implementação por CPU , esse modo usa os valores iniciais aleatórios de pontos de entrada e ponderações de seleção configuradas para selecionar aleatoriamente a malha de cada instância. Essas malhas devem ser definidas no nó e não orientadas por atributo.
O sistema usa ponderações para determinar quantas instâncias devem ser alocadas para cada malha. Uma sobrealocação é feita com base em uma heurística para minimizar a chance de saturar a alocação para uma ou mais primitivas e perder instâncias.
Esse modo depende do atributo Seed em pontos bem inicializados, por exemplo, usando a função de shader `ComputeSeedFromPosition()` fornecida. Se todas as propagações de pontos estiverem definidas com o mesmo valor, a mesma seleção será feita para todos os pontos, e a alocação estimada poderá ser excedida, fazendo com que instâncias fiquem ausentes no resultado.
Por atributo (PCGMeshSelectorByAttribute)
Outros tipos de seletores de malha (por exemplo, PCGMeshSelectorWeightedByCategory) não são compatíveis com a execução na GPU.
Para inspecionar a contagem final de instâncias alocadas, selecione os componentes de malha estática instanciados de maneira procedural gerados e verifique a propriedade Número de instâncias.
Empacotamento de dados de instâncias
De modo semelhante à execução na CPU, os atributos podem ser empacotados em dados de instâncias.
O sistema precisa saber, antes da execução da GPU, quantos atributos serão empacotados. Portanto, apenas o tipo de empacotador por atributo (PCGInstanceDataPackerByAttribute) é compatível.
Outros nós
Atualmente, os seguintes nós são compatíveis com a execução na GPU:
Copiar pontos
Partição de atributos
No momento, apenas há suporte para o particionamento em atributos do tipo String, Caminho de objeto flexível ou Caminho de classe flexível.
Normal para densidade
Contagem de dados
Gerador de malha estática
HLSL personalizado
Execução de CPU sem suporte.
Fontes de computação
Ativos de Fontes de computação facilitam o compartilhamento do código-fonte e reduzem a duplicação de código entre nós.
Os ativos permitem edição em linha do código-fonte HLSL com destaque de sintaxe e sintaxe específica para PCG, como rótulos de dados e nomes de atributos.
Ativos de Fonte de computação também podem fazer referência outros ativos de Fonte de computação usando a propriedade Fontes adicionais, que cria hierarquias de dependências entre várias fontes de computação.
Rótulos de dados
Rótulos de dados podem ser usados para referenciar dados por rótulo em vez de por índice na fonte do HLSL personalizado. Os rótulos de dados são comunicados aos dados por meio de tags com o prefixo PCG_DATA_LABEL.
Alguns nós rotulam os dados de saída de forma automática, incluindo:
Obter dados de textura
Obter dados de textura virtual
Gerar mapas de grama
Como gerar mapas de grama
A PCG permite a amostragem de camadas de grama de paisagem a partir de um material de paisagem específico para permitir fluxos de trabalho de geração procedural em tempo de execução.
Configure o material de Paisagem com um nó de Saída de grama de paisagem. Para obter mais informações sobre como configurar um material de paisagem, consulte Materiais de paisagem.
Conecte seus dados de Paisagem a um nó Gerar mapas de grama. Selecione os tipos de grama desejados usando substituição ou por meio de exclusão.
Obtenha amostras das texturas do mapa de grama. É possível obter amostras por índice ou por rótulos de dados atribuídos automaticamente. O seguinte código foi adicionado ao campo Fonte do shader da janela Editor de código-fonte do 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;
Se você pretende obter amostras das texturas do mapa de grama apenas na GPU, pode ativar Ignorar retorno de leitura para CPU no gráfico de PCG para melhorar significativamente o desempenho.
Camadas de paisagem pintadas:
Geração resultante:
Usar texturas virtuais na PCG
A PCG permite o uso de texturas virtuais como parte do fluxo de trabalho de geração procedural de conteúdo.
Amostragem de texturas virtuais
Texturas virtuais podem ser amostradas a partir de dados de paisagem para melhorar o desempenho da amostragem de altura.
Exemplo 1: Dados da paisagem
Para amostra de dados de paisagem usando texturas virtuais, certifique-se de que Texturas de virtuais de amostra" está habilitado nas configurações do nó Get Landscape Data. Isso permite que o nó Landscape Data use texturas virtuais fornecidas pelo material de paisagem correspondente.
Isso afeta apenas a amostragem da 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);
Exemplo 2: Dados de texturas virtuais
Para obter uma amostra de uma textura virtual, consulte o mundo em busca de componentes de textura virtual em tempo de execução. Cada uma produzirá dados de textura virtual, etiquetados com um rótulo de dados para o ativo de textura virtual em tempo de execução.
float3 Position = CreateGrid2D(ElementIndex, NumPoints, GetComponentBoundsMin(), GetComponentBoundsMax());
// Sample virtual textures
bool bInsideVolume;
float3 BaseColor;
float Specular;
float Roughness;
float WorldHeight;
float3 Normal;
float Displacement;
Preparação da textura virtual
É importante garantir que as texturas virtuais sejam preparadas antes da geração, caso contrário, os resultados de amostragem poderão ser imprecisos.
Para solicitar a preparação da textura virtual, adicione um parâmetro de gráfico do tipo FPCGVirtualTexturePrimingInfo ao gráfico de PCG. Isso expõe as seguintes opções:
Textura virtual | Define a textura virtual a ser preparada. |
Grade | Define a maior grade na qual a textura virtual é amostrada no gráfico. As texturas virtuais são preparadas para o raio de geração determinado por essa grade. |
Tamanho de texel do mundo | Define o tamanho desejado de um texel na textura virtual preparada. Isso determinará qual nível de mapa de mip deve ser preparado. |
Você pode controlar a preparação de textura virtual usando o comando do console pcg.VirtualTexturePriming.Enable. Você pode depurar essa funcionalidade usando o comando pcg.VirtualTexturePriming.DebugDrawTexturePrimingBounds.