Até recentemente, a grama nos jogos era geralmente indicada por uma textura no chão, em vez de renderizar caules individuais. Mas com o aumento do poder do ferro, tornou-se possível produzir grama. Grandes exemplos dessa renderização podem ser vistos em jogos como
Horizon Zero Dawn e
The Legend of Zelda: Breath of the Wild . Nesses jogos, o jogador pode percorrer os prados de grama e, mais importante, a grama
responde às ações do jogador.
Felizmente, criar um sistema desse tipo não é muito difícil. De fato, o artigo ensinará exatamente isso! Neste tutorial, você aprenderá o seguinte:
- Crie um campo vetorial usando um sistema de captura de cenas e partículas
- Dobre a grama de um jogador com base em um campo vetorial
Nota: este tutorial pressupõe que você já conhece os conceitos básicos de trabalho com o Unreal Engine. Se você é novo no Unreal Engine, confira nossa série de dez partes de tutoriais do Unreal Engine para iniciantes . Em particular, consulte o tutorial Particle Systems para saber como usar o Cascade neste tutorial.
Nota: este tutorial faz parte de uma série de três tutoriais sobre como trabalhar com destinos de renderização:
Começando a trabalhar
Vamos começar baixando os materiais deste tutorial (você pode baixá-los
aqui ). Descompacte-os, vá para
InteractiveGrassStarter e abra
InteractiveGrass.uproject . Você verá um pequeno campo de grama que será o tema deste tutorial. Também criei um widget para exibir o destino de renderização da captura de cena.
Antes de começar, você deve estudar o tutorial
sobre como criar pegadas na neve , porque vou pular algumas informações. Vale a pena considerar que a captura e a projeção também são usadas neste tutorial. Para economizar tempo, preparei uma captura de planta, semelhante à planta do tutorial sobre as trilhas na neve.
Para começar, vejamos outra maneira de criar grama interativa. A maneira mais comum é transferir as coordenadas do jogador para o material da grama e usar uma máscara esférica para dobrar a grama em um determinado raio do jogador.
Embora essa abordagem seja muito boa, ela não aumenta quando queremos adicionar mais atores à grama. Para cada ator que você adicionar, você precisará adicionar outro parâmetro de coordenada e uma máscara esférica ao material. Um método mais escalável é usar um
campo vetorial .
O que é um campo vetorial?
Um campo vetorial é apenas uma textura, cada pixel corresponde a uma direção. Se você costumava trabalhar com mapas de fluxo, eles são semelhantes. Mas, em vez de mover UV, usaremos o contato
Deslocamento da posição mundial para mover os vértices. Ao contrário da solução com uma máscara esférica, para obter a direção da flexão, é suficiente que os materiais provem o campo vetorial apenas
uma vez .
Vamos descobrir como você pode salvar as direções em uma textura. Veja esta grade:
Digamos que o ponto vermelho é o objeto que queremos mover. Se o movermos para o canto inferior direito, qual vetor denotará esse movimento? Se você respondeu
(1, 1) , está certo! Como você provavelmente sabe, também pode representar vetores como flores e, assim, salvá-los em textura. Vamos inserir esse vetor no seletor de cores Unreal e ver qual cor ele retorna.
Como você pode ver, a direção
(1, 1) retorna em amarelo. Isso significa que, se queremos dobrar a grama na direção dos eixos XY positivos, teremos que usar essa cor de textura. Vamos agora olhar para as cores de todos os vetores.
O quadrado inferior direito parece muito bom porque possui gradientes nos dois eixos. Isso significa que podemos armazenar qualquer vetor nesse quadrante como uma cor, porque cada vetor tem uma cor única.
Mas há um problema com os outros três quadrantes. Temos apenas um gradiente ao longo de um eixo, ou nenhum gradiente. Isso significa que vários vetores terão a mesma cor. Por exemplo, não podemos distinguir entre os vetores
(-1, 1) e
(0, 1) .
Esses três quadrantes não têm cores exclusivas para cada vetor, porque só podemos representar cores usando valores de 0 a 1. no entanto, esses três quadrantes usam valores negativos fora desse intervalo.
A solução é redistribuir os vetores para que todos eles se ajustem no intervalo de 0 a 1. Isso pode ser feito multiplicando o vetor por
0,5 e adicionando
0,5 . Aqui está uma visualização do que obtemos:
Agora cada vetor tem uma cor única. Quando precisamos usá-lo para cálculos, simplesmente o redistribuímos de volta para o intervalo de -1 a 1. Aqui estão algumas cores e as direções correspondentes após a redistribuição:
- (0, 0): X e Y negativos
- (0,5, 0,5): sem movimento
- (0, 1): X negativo e Y positivo
- (1, 0): X positivo e Y negativo
Agora vamos aprender como criar um campo vetorial no Unreal.
Criando um campo vetorial
Ao contrário de criar traços na neve, não realizaremos a captura da forma dos objetos. Em vez disso, pintaremos o alvo de renderização com pincéis. Estas serão apenas imagens do campo vetorial selecionado. Vou chamá-los de
pincéis de direção .
Em vez de desenhar no alvo de renderização com embotamentos, podemos usar
partículas . As partículas exibirão um pincel de instruções e emitidas pelo player. Para criar um campo vetorial, basta usar a captura de cena e capturar apenas partículas. A vantagem deste método é que a criação de rastreios é muito simples. Além disso, permite gerenciar facilmente propriedades como a duração do armazenamento de trilhas e seu tamanho. Além disso, as partículas criam traços temporariamente persistentes, porque existem após deixar a área de captura e retornar a ela.
Abaixo estão alguns exemplos de pincéis direcionais que podemos usar, bem como seus efeitos na grama. Observe que no exemplo abaixo, as partículas são invisíveis.
Para começar, vamos criar um material que exibirá um pincel de direção.
Criando material para direções
Existem duas maneiras de criar um pincel de direção:
- Matemático: direções e forma são definidas dentro do material. Sua vantagem é que ele não requer software de terceiros e é conveniente para formulários simples.
- Conversão normal de mapa: crie um mapa normal das direções e formas desejadas. Para converter o mapa em um campo vetorial adequado, basta remover o canal azul. A vantagem desse método é que você pode facilmente criar formas complexas. Abaixo está um exemplo de um pincel que será difícil criar matematicamente.
Para este tutorial, criaremos um pincel matematicamente. Vá para a pasta
Materiais e abra
M_Direction . Observe que o modelo de sombreamento
Unlit está selecionado para este material. Isso é importante porque permite a captura de uma cena para capturar partículas sem afetá-las.
Para não complicar, criaremos um material que fará com que a grama se afaste do centro da partícula. Para fazer isso, crie o seguinte esquema:
Agora precisamos redistribuir. Para fazer isso, adicione os nós selecionados e conecte tudo da seguinte maneira:
Agora vamos dar ao pincel uma forma redonda. Para fazer isso, adicione os nós selecionados:
RadialGradientExponential controla o tamanho e a nitidez da circunferência do círculo. A multiplicação pela
cor de partículas permite controlar a opacidade das partículas do sistema de partículas. Vou discutir isso em mais detalhes na próxima seção.
Aqui está a aparência do pincel:
Clique em
Aplicar e feche o material. Agora que criamos o material, é hora de embarcar em um sistema de traços de partículas.
Criando um sistema de traços de partículas
Vá para a pasta
ParticleSystems e abra
PS_GrassTrail . Para economizar tempo, eu já criei todos os módulos necessários.
Veja como cada módulo afeta as pegadas da grama:
- Spawn: a frequência da criação afeta a suavidade das faixas. Se as faixas parecerem intermitentes, você deverá aumentar a frequência de criação. Neste tutorial, deixaremos o valor padrão (20).
- Vida útil : a vida útil da trilha até que a grama retorne ao seu estado original
- Tamanho inicial: tamanho do traço
- Cor ao longo da vida: como usamos a cor de partículas no material, você pode controlar a opacidade aqui. Você também pode alterar a curva alfa para controlar o desaparecimento da faixa. Por exemplo, você pode escolher o desaparecimento linear, facilitando a entrada e / ou saída. Neste tutorial, deixaremos a configuração padrão, ou seja, perda linear.
- Eixo de bloqueio: usado para direcionar partículas em direção à captura de cena
- Rotação inicial: usada para garantir que as partículas sejam orientadas ao longo dos eixos corretos (mais sobre isso abaixo)
Primeiro, precisamos definir o material. Selecione o módulo
Necessário e defina
Material como
M_Direction . Defina também o
Modo de classificação como
PSORTMODE Age Newest First .
Este modo de classificação permite renderizar novas partículas sobre as antigas. Se isso não for feito, não será novo, mas as partículas antigas afetarão a grama.
A seguir, é a duração do rastreamento. Selecione o módulo
Lifetime e defina
Constant como
5 . Graças a isso, o rastreamento desaparecerá dentro de cinco segundos.
Agora vamos para o tamanho do rastreamento. Selecione o módulo
Tamanho inicial e defina a
constante como
(150, 150, 0) . Devido a isso, cada partícula cobrirá uma área de 150 × 150.
Agora precisamos fazer com que pareça na direção de capturar a cena. Como a cena é capturada de cima, as partículas devem olhar na direção do eixo Z positivo. Para fazer isso, selecione o módulo
Lock Axis e defina os
Flags Lock Axis como
Z.Finalmente, precisamos definir a rotação das partículas. Atualmente, as cores do pincel não estão alinhadas com a direção que representam. Isso aconteceu porque, por padrão, o sistema de partículas é aplicado com uma rotação de 90 graus. Para corrigir isso, selecione o módulo
Rotação Inicial e defina o valor
Constante como
-0,25 . Isso irá girar as partículas 90 graus no sentido anti-horário.
E é tudo o que precisamos para o sistema de partículas, então vamos fechá-lo.
Em seguida, precisamos anexar um sistema de partículas ao que é suposto criar traços. No nosso caso, precisamos anexá-lo ao personagem do jogador.
Anexando sistema de partículas
Vá para
Characters \ Mannequin e abra
BP_Mannequin . Em seguida, crie o componente
Sistema de partículas e
denomine GrassParticles .
Em seguida, precisamos definir um sistema de partículas. Vá para o painel Detalhes e defina
Particles \ Template como
PS_GrassTrail .
Seria estranho se o jogador pudesse ver a pista no jogo, então vale a pena escondê-la. Para fazer isso, ative
Rendering \ Owner No See .
Como o sistema de partículas está conectado ao jogador (proprietário), o jogador não o verá, mas ficará visível para todo o resto.
Clique em
Compilar e, em seguida, clique em
Executar . Observe que as partículas não aparecem na câmera do jogador, mas são exibidas no alvo de renderização.
Até agora, a captura de cena está configurada para capturar
tudo . Obviamente, isso não nos convém, porque apenas partículas afetam a grama. Na próxima seção, aprenderemos como capturar apenas partículas.
Captura de partículas
Se capturarmos partículas agora, realizaremos flexões desnecessárias em áreas sem partículas. Isso ocorre porque a cor de fundo do destino de renderização é preta. A flexão ocorre porque o preto denota um movimento em direção aos eixos XY negativos (após a redistribuição). Para que as áreas vazias não contenham movimento, precisamos renderizar o destino do plano de fundo
(0,5, 0,5, 0) . A maneira mais fácil de fazer isso é criar um avião enorme e anexá-lo ao jogador.
Primeiro, crie material para o plano de fundo. Volte ao Navegador de conteúdo e abra
Materials \ M_Background . Em seguida, conecte a constante
(0,5, 0,5, 0) com a
Emissive Color .
Nota: como no material de partículas, qualquer material que capturamos deve ter um modelo de shader apagado .
Clique em
Aplicar e feche o material. Volte para
BP_Mannequin e crie um novo componente de
avião . Nomeie-o como
plano de fundo .
Em seguida, defina as seguintes propriedades:
- Localização: (0, 0, -5000). Colocamos o plano tão baixo que não sobrepõe nenhuma partícula.
- Escala: (100, 100, 1). Então, escalamos para um tamanho suficiente para cobrir toda a área de captura.
- Material: M_Background
Assim como as partículas, seria estranho se o jogador visse um enorme avião amarelo por baixo. Para ocultá-lo, ative
Rendering \ Owner No See .
Agora que configuramos o plano de fundo, é hora de capturar as partículas. Podemos fazer isso adicionando um sistema de partículas à
lista apenas de exibição da captura de cena. Esta é uma lista de componentes que capturam a captura de cena.
Usando a lista Show-Only
Antes de podermos adicionar à lista de exibição apenas, precisamos de uma maneira de obter todos os atores que afetam a grama. Uma maneira de obtê-los é usar
tags . Tags são cadeias simples que podem ser atribuídas a atores e componentes. Em seguida, você pode usar o nó Obter todos os atores com tag para obter todos os atores com a tag correspondente.
Como o ator do jogador deve influenciar a grama, ele precisa de uma etiqueta. Para adicionar uma marca, clique no botão
Padrões da
classe . Em seguida, crie uma nova tag em
Actor \ Tags e
chame -a de
GrassAffector .
Como apenas os
componentes podem ser transferidos para a lista de exibição apenas, precisamos adicionar tags aos
componentes que afetam a grama. Selecione o componente
GrassParticles e adicione uma nova tag localizada na seção
Tags . Nomeie-o
GrassAffector também (não é
necessário usar essa tag). Repita o mesmo para o componente
Background .
Agora, precisamos adicionar componentes que influenciam a grama à lista apenas de exibição de captura de cena. Clique em
Compilar e feche
BP_Mannequin . Em seguida, abra
Blueprints \ BP_Capture . Vá para
Event BeginPlay e adicione os nós destacados. Verifique também se os pinos marcados estão conectados.
Este circuito percorrerá todos os atores com a tag
GrassAffector . Depois disso, ela verificará se o ator tem componentes com essa tag e os adicionará à lista de exibição apenas.
Em seguida, precisamos dizer à captura de cena para usar apenas a lista de exibição apenas. Selecione o componente
SceneCapture e vá para a seção
Captura de cena . Defina
o Modo de renderização primitivo para
usar a lista ShowOnly .
Clique em
Compilar e feche o blueprint. Se você clicar em
Reproduzir , verá que o alvo de renderização agora captura apenas partículas e o plano de fundo.
Na próxima seção, chegamos ao que esperávamos. É hora de ensinar a grama a se curvar!
Dobre a grama
Primeiro, precisamos projetar o alvo de renderização na grama. Vá para a pasta
Materiais e abra
M_Grass . Em seguida, crie os nós mostrados abaixo. Defina a textura como
RT_Capture .
Como redistribuímos as cores no intervalo de 0 a 1, antes de usá-las, precisamos redistribuir as cores novamente no intervalo de -1 a 1. Para fazer isso, adicione os nós destacados:
Agora que temos a direção de flexão, precisamos de uma maneira de girar a grama nessa direção. Felizmente, existe um nó chamado
RotateAboutAxis para isso. Vamos criá-lo.
Vamos começar com o contato
NormalizedRotationAxis . Como o nome indica, esse é o eixo em torno do qual o vértice girará. Para calcular, precisamos apenas de um
produto vetorial com a direção da dobra por
(0, 0, -1) . Para fazer isso, precisamos adicionar os nós selecionados:
Também precisamos especificar o
RotationAngle , ou seja, a quantidade de rotação do vértice em relação ao ponto de rotação. Por padrão, o valor deve estar entre 0 e 1, onde 0 é 0 graus e 1 é 360 graus. Para obter o ângulo de rotação, podemos usar o comprimento da direção de flexão, multiplicado pela rotação máxima.
Multiplicar por uma rotação máxima de
0,2 significa que o ângulo máximo de rotação é de
72 graus.
O cálculo do
PivotPoint é um pouco mais difícil, porque uma única malha de grama contém vários caules. Isso significa que não podemos usar algo como o nó
Posição do
objeto , porque ele retornará um único ponto para todas as hastes da grama.
Idealmente, você deve usar um editor 3D de terceiros para salvar pontos de articulação dentro dos canais UV. Mas para este tutorial, apenas aproximamos o ponto de articulação. Isso pode ser feito movendo-se de cima para
baixo para um determinado deslocamento.
Para fazer isso, adicione os nós selecionados:
Neste tutorial, a grama tem cerca de
80 unidades de altura, então eu configurei esse valor para
PivotOffset .
Em seguida, precisamos executar duas máscaras. A primeira máscara garante que a raiz da haste não se mova. A segunda máscara garante que nenhum campo de vetor atue na grama fora da área de captura.
Mascaramento
Para este tutorial, defino as cores dos topos da grama para que os vértices inferiores sejam pretos e os vértices superiores sejam brancos.
Para mascarar as raízes, simplesmente multiplicamos o resultado de
RotateAboutAxis pelo nó
Vertex Color .
Para mascarar a grama fora da área de captura, multiplicamos o resultado anterior pelo nó selecionado:
Clique em
Aplicar e feche o material. Clique em
Play e corra pela grama para deixar pegadas nela!
Para onde ir a seguir?
O projeto finalizado pode ser baixado
aqui .
Embora o método mostrado no tutorial funcione muito bem para objetos simples, como grama, não é suficiente ao usar objetos que exigem mais dinamismo. Uma solução parcial para o problema é usar as versões físicas da folhagem. Veja
este post sobre folhagem interativa para obter mais informações.
Finalmente, quero agradecer a Deathrey, da comunidade Unreal Engine, pela dica desse método de partículas!