Renderização na Unreal Engine
Introdução à renderização em engines de jogo
Renderização refere-se ao processo de geração de uma imagem final (quadro) na tela a partir de um conjunto de objetos em uma cena.
O software usado para renderizar um quadro é chamado de engine de renderização, e essas engines são normalmente categorizadas como:
-
Renderização off-line: destina-se a renderização de alto nível que prioriza a qualidade em relação ao tempo de processamento. Normalmente, essas engines são usadas em aplicativos em que o tempo de renderização não é importante em comparação com a qualidade do quadro renderizado final.
-
Renderização em tempo real: projetada para priorizar o desempenho e renderizar quadros rapidamente. As metas comuns de taxa de quadros em tempo real são 30 (33 ms), 60 (16 ms) e 120 (8 ms) quadros por segundo (FPS), mas as taxas de quadros reais podem variar com o tempo, dependendo de diversos fatores. Projetos desenvolvidos com renderização em tempo real precisam encontrar um equilíbrio entre desempenho e qualidade para manter taxas de quadros consistentes. As engines de renderização em tempo real são normalmente usadas para mídia interativa, como videogames, simulações e visualização arquitetônica.
A Unreal Engine é um conjunto avançado de ferramentas projetado para oferecer renderização em tempo real e atender às necessidades de uma variedade de plataformas, desde dispositivos móveis até computadores avançados. A Unreal Engine é capaz de renderizar em tempo real e off-line com alta qualidade. Você pode usá-la para criar de tudo, desde experiências interativas em 2D e 3D em dispositivos móveis, consoles e computadores até a renderização de quadros finais para produções de cinema e televisão.
Diferentemente de outras engines em tempo real no mercado, a Unreal Engine oferece muitas funcionalidades próprias criadas especificamente para proporcionar maior desempenho e renderização em tempo real. O objetivo é reduzir a complexidade do desenvolvimento e obter resultados mais rapidamente, mantendo a alta qualidade e o desempenho.
O sistema de iluminação global e reflexos Lumen, a geometria virtualizada Nanite e os Mapas de Sombras Virtuais são etapas importantes para atingir esse objetivo de eliminar a complexidade durante o desenvolvimento com funcionalidades que "simplesmente funcionam" para aplicativos de console e desktop. Os dispositivos móveis são compatíveis com iluminação dinâmica e fluxos de trabalho de iluminação pré-calculados que exigem que você incorpore iluminação a texturas.
Introdução à renderização na Unreal Engine
As engines de jogo executam uma série de etapas, geralmente chamadas de pipeline de renderização, para renderizar uma imagem (ou quadro) na tela. Esta seção descreve como a Unreal Engine faz isso usando o próprio caminho de renderização diferida padrão e compara as etapas com o caminho de renderização diferida do Unity, quando apropriado.
A engine do Unity conta com três pipelines de renderização distintos: Integrado, Universal e de Alta Definição. Cada pipeline destina-se a casos de uso específicos e, normalmente, é selecionado antes do início de um projeto.
A Unreal Engine conta com um pipeline de renderização unificado, que dimensiona funcionalidades individuais com base na plataforma de destino: desde dispositivos portáteis e móveis até a geração atual de consoles e PCs. Isso significa que você pode escolher um caminho de renderização e as funcionalidades compatíveis que melhor se adaptam a um projeto, sem se limitar a um único caminho.
O pipeline de renderização da Unreal Engine pode ser usado com o próprio caminho padrão de renderização diferida ou pode ser configurado para ser executado em um caminho de renderização direta. Além disso, você pode habilitar o caminho de renderização móvel para acomodar dispositivos de menor potência, incluindo o Vulkan Mobile Renderer. Para saber mais sobre as funcionalidades de renderização compatíveis com cada caminho de renderização, leia a documentação Supported Features by Rendering Path (Funcionalidades compatíveis por caminho de renderização).
A imagem abaixo mostra uma visualização de alto nível das etapas que a Unreal Engine executa em cada quadro para renderizar uma imagem final usando o caminho de renderização diferida:
O processo flui da esquerda para a direita, e as etapas 2 a 5 ocorrem em paralelo.
A seguir, apresentaremos mais informações sobre cada uma dessas etapas do pipeline de renderização e o que é necessário para renderizar cada quadro.
Preparação da cena e oclusão
A Unreal Engine tem três threads principais: os threads Game (CPU), Draw e GPU.
Antes de iniciar o processo de renderização, o thread Game (ou CPU) reúne as transformações de todos os objetos na cena. Isso inclui o processamento de todas as animações, simulações de física e inteligência artificial (IA) antes de reunir a transformação final de cada objeto.
As informações de transformação são, então, passadas para o thread Draw na CPU. O thread Draw executa o processo de delimitação, que cria uma lista de objetos visíveis na visualização atual da câmera e remove todos os outros objetos que não são visíveis para a câmera. Esses objetos não precisam ser desenhados, e não renderizá-los melhora o desempenho.
Esse processo executa as seguintes etapas (em ordem):
- Delimitação de distância: remove todos os objetos que estão mais distantes do que uma distância específica da câmera.
- Delimitação de frustum: remove objetos que não são visíveis dentro (da visão) do frustum da câmera.
- Delimitação de oclusão: verifica com precisão o estado de visibilidade de todos os objetos restantes na cena. Esse método é dispendioso e, portanto, é executado no final do processo de oclusão, em que os objetos visíveis restantes são testados para ver se estão ocluídos (ocultos) por outros objetos.
A lista final de objetos visíveis é transmitida para o thread GPU para iniciar o processo de renderização.
Equivalente no Unity
O Unity executa o frustum e a delimitação de oclusão durante o próprio pipeline de renderização. Além disso, ele pode executar a delimitação de distância com sua API CullingGroup. Uma combinação dessas técnicas cria a lista final de objetos visíveis na cena.
Renderização de geometria
Nesta etapa, a Unreal Engine percorre a lista de objetos visíveis na cena e os prepara para a próxima etapa, na qual converte os dados de vértices 3D em dados de pixels que são exibidos na tela.
Shaders de vértice
Um shader é uma parte do código que é executada diretamente na GPU e é usada para realizar um conjunto de cálculos. Eles são eficientes, e a GPU pode executar muitos cálculos de shader em paralelo.
O shader de vértices executa as seguintes etapas:
- Converte posições de vértices locais na posição do mundo: os dados de vértices do objeto são armazenados no espaço local, mas, quando o objeto é colocado no mundo, as informações de vértices devem ser convertidas em coordenadas do espaço do mundo.
- Manipula o sombreamento e a coloração dos vértices: o shader de vértices lida com a suavização de vértices, bem como com qualquer dado de cor de vértice no próprio objeto.
- Pode aplicar deslocamentos adicionais a posições de vértices: o shader de vértices pode deslocar a posição de qualquer vértice na tela para obter efeitos específicos. Isso é feito por meio do material do objeto e é chamado de world position offset deslocamento de posição no mundo.
Passagem de profundidade
Antes de renderizar objetos individuais, a Unreal Engine executa uma passagem de profundidade, ou passagem de Z antecipada, para determinar a localização dos objetos em relação uns aos outros. Isso evita uma situação em que a Unreal Engine esteja renderizando os mesmos pixels na tela várias vezes, o chamado overdraw, que pode ter um impacto significativo no desempenho. A engine tenta evitar isso o máximo possível.
Draw calls
Após a passagem de profundidade, a GPU renderiza cada objeto desenhando todos os polígonos que compartilham as mesmas propriedades ao mesmo tempo, como malhas e materiais. Essa ação é conhecida como draw call.
Todos os polígonos de um objeto aos quais é atribuído o mesmo material são contados como a mesma draw call. No entanto, cada material exclusivo requer a própria draw call separada. Por exemplo, cada objeto na tela requer, no mínimo, uma draw call, mas pode ter mais, dependendo do número de materiais atribuídos ao objeto.
A Geometria virtualizada do Nanite da Unreal Engine renderiza todos os polígonos de todos os objetos com um determinado material ao mesmo tempo. * Os orçamentos de quadros não estão mais limitados por números de polígonos, draw calls nem uso de memória de malhas.
Equivalente no Unity
O fluxo de trabalho de renderização do Unity executa etapas semelhantes, nas quais faz uma passagem de profundidade e usa draw calls para desenhar os objetos na cena.
Rasterização e buffer de geometria
O processo de rasterização converte dados de vértices 3D em dados de pixels 2D que são exibidos na tela. Esse processo começa depois que o shader de vértices termina de processar todos os dados.
O buffer de geometria (GBuffer) da Unreal Engine inclui uma série de imagens que armazenam informações sobre a geometria da cena. Essas imagens normalmente incluem informações de iluminação para os seguintes itens na cena: cor de base, normal do mundo, metálico, rugosidade e especular. Essas imagens no GBuffer são compostas para formar a imagem final que você vê na tela.
O processo de conversão dessas imagens compostas ocorre para cada quadro que é renderizado e para cada draw call em que os dados de vértices são convertidos em dados de pixels e desenham as partes corretas das imagens no GBuffer.
Equivalente no Unity
O caminho de renderização diferida do Unity também usa um GBuffer para armazenar informações críticas sobre a cena. No caso do Unity, o GBuffer armazena informações semelhantes sobre a cena (referenciadas com nomes diferentes): informações de albedo, especulares, de normal e de emissivos/iluminação para os objetos.
Renderização de texturas
A Unreal Engine renderiza texturas usando transmissão de texturas para otimizar o carregamento de texturas na cena. O sistema de transmissão de texturas utiliza mipmaps de textura, que são sequências pré-calculadas de imagens da mesma textura em resoluções diferentes. Você pode pensar nisso como níveis de detalhes (LODs) para texturas em vez de malhas. A engine cria automaticamente esses mipmapas, em que cada imagem tem metade da resolução da anterior.
A Unreal Engine transmite o mipmap da textura durante o jogo com base na distância em relação à câmera. Isso é feito automaticamente para otimizar a largura de banda e o consumo de memória, além de reduzir o ruído a uma maior distância da câmera.
Os tamanhos de textura devem ser uma potência de 2 para receber mipmaps. Os tamanhos comuns de textura incluem 3840 x 2160 pixels (4K) e 1920 x 1080 pixels (HD). Observe que não é necessário que as texturas tenham uma proporção específica; uma textura de 1920 x 480 pixels também receberá mipmaps.
Equivalente no Unity
O sistema Mipmap Streaming do Unity usa mipmaps de textura para transmitir as texturas no tempo de execução. Da mesma forma que a Unreal Engine, esse sistema transmite o mipmap de textura correto automaticamente com base na distância e no ângulo em relação à câmera.
Shaders de pixels e materiais
Depois que os objetos tiverem sido totalmente renderizados no GBuffer, a Unreal Engine começará a sombrear cada objeto na tela usando as propriedades de material de cada objeto com o shader de pixels.
Um shader de pixels executa uma série de cálculos para modificar a cor de um pixel na tela. Executados na GPU, os shaders de pixels são extremamente eficientes. Eles acionam o sistema de materiais da Unreal Engine e são usados no cálculo de iluminação, neblina, reflexos e efeitos de pós-processamento.
O sistema de materiais usa modelos de shaders HLSL (High-Level Shader Language) juntamente com o Editor de Materiais para criar os materiais finais que são aplicados aos objetos na tela. Esses materiais podem usar parâmetros, como texturas, para definir a aparência de cada objeto.
Equivalente no Unity
O Unity conta com vários shaders pré-integrados (equivalentes a materiais na Unreal Engine), juntamente com o Shader Graph para criar shaders em projetos. O Editor de Materiais da Unreal Engine é o equivalente ao Shader Graph do Unity.
Reflexos
Depois de sombrear todos os objetos da cena, a Unreal Engine começa a renderizar os reflexos desses objetos com base nas propriedades dos respectivos materiais.
A Unreal Engine usa quatro sistemas para renderizar os reflexos na cena. Esses sistemas são executados na seguinte ordem:
- Capturas de reflexos: pré-calculadas e armazenadas em um Cube Map estático em um local específico.
- Reflexos planares: capturam reflexos gerados por um plano e que incidem nele.
- Reflexos do espaço de tela (Screen Space Reflections, SSR): usam as informações disponíveis na tela para traçar reflexos precisos de objetos em tempo real.
- Reflexos Lumen: resolvem reflexos para toda a gama de valores de rugosidade na cena. Esses reflexos incluem compatibilidade com luz do céu, materiais de revestimento transparente, translucidez e até mesmo materiais de água de camada única.
A Unreal Engine mescla os três métodos, dando prioridade aos reflexos do espaço de tela, depois voltando aos reflexos planares e, por fim, voltando a capturas de reflexos. O resultado final do reflexo é combinado com as imagens de rugosidade, especulares e metálicas no GBuffer.
Se você estiver usando a Iluminação global Lumen, os reflexos Lumen serão usados automaticamente. No entanto, você também pode usar reflexos Lumen sem a IG Lumen. Nesse caso, a Unreal Engine usará a iluminação incorporada com reflexos Lumen.
Equivalente no Unity
As Reflection Probes do Unity oferecem funcionalidade semelhante e são usadas para pré-calcular dados de reflexo para sua cena.
Iluminação estática e sombras
Depois que os reflexos são renderizados, a Unreal Engine renderiza a iluminação estática e as sombras de todos os objetos na cena.
A Unreal Engine usa o sistema de Iluminação global Lightmass para pré-calcular as informações de iluminação da cena. As informações de iluminação e sombras são armazenadas em uma textura UV de mapa de iluminação, e essa textura é mesclada com a cor de base do objeto ao renderizar o objeto na cena.
Esse sistema é muito rápido, mas requer mais memória e precisa ser pré-calculado sempre que há uma alteração na cena.
O sistema de Iluminação global Lightmass é uma boa opção para projetos voltados a dispositivos móveis e de baixa potência.
Equivalente no Unity
Os sistemas Progressive Lightmapper e Enlighten Baked Global Illumination do Unity oferecem funcionalidade semelhante ao pré-calcular a iluminação para sua cena.
Iluminação e sombras dinâmicas
Depois que a iluminação estática é renderizada, a Unreal Engine renderiza a iluminação e as sombras dinâmicas (em tempo real) com o Lumen, seu sistema de iluminação global dinâmica.
O Lumen é um sistema totalmente dinâmico de iluminação global e reflexos projetado para consoles de última geração e PCs de ponta. O sistema usa vários métodos de traçado de raios para resolver a iluminação global e os reflexos em grande escala.
O Lumen oferece infinitos rebates difusos e é totalmente compatível com a geometria virtualizada do Nanite. Além disso, o sistema funciona em conjunto com Mapas de Sombras Virtuais para criar sombras suaves de alta resolução e em tempo real.
O Lumen fornece reflexos Lumen que resolvem reflexos para toda a gama de valores de rugosidade na cena. Esses reflexos incluem compatibilidade com luz do céu, materiais de revestimento transparente, translucidez e até mesmo materiais de água de camada única.
O Lumen substitui os reflexos do espaço de tela quando usado na cena.
Equivalente no Unity
O Unity usa Enlighten Realtime Global Illumination para fornecer iluminação dinâmica na cena. Esse sistema fornece iluminação global em tempo real usando informações de visibilidade pré-calculadas, juntamente com mapas de iluminação para calcular o rebate de luz indireta no tempo de execução.
Isso difere do Lumen, pois este não exige dados pré-calculados para fornecer rebate de luz indireta.
Neblina e transparência
Depois de renderizar a iluminação e as sombras dinâmicas, a Unreal Engine renderiza os efeitos de neblina e transparência.
Ela renderiza efeitos de neblina com o sistema Neblina de altura exponencial, que renderiza a densidade da neblina com base na altura e na distância em relação à câmera. Além disso, o sistema pode gerar neblina volumétrica.
Os objetos transparentes usam um material translúcido e são renderizados nesse estágio do processo. Ao usar o caminho de renderização diferida, a Unreal Engine utiliza as informações disponíveis no GBuffer para renderizar a transparência. Como alternativa, você pode configurar o material para usar o caminho de renderização avançado a fim de produzir um efeito de transparência mais preciso.
Equivalente no Unity
O Unity oferece compatibilidade com os tipos de neblina Linear, Exponential e Exponential Squared na cena.
Efeitos de pós-processamento
Depois que a neblina e a transparência são renderizadas, a UE pode aplicar efeitos adicionais sobre a imagem. Esses efeitos são chamados de efeitos de pós-processamento, pois são aplicados após o processamento da imagem final. Os efeitos dependem do shader de pixels e usam as informações disponíveis no GBuffer.
Alguns efeitos comuns de pós-processamento incluem bloom de luz, profundidade de campo, fachos de luz, mapeamento de tons e desfoque de movimento.
Como parte dessa etapa de pós-processamento, a Unreal Engine pode aplicar a Temporal Super Resolution (TSR). A TSR é um upscaler temporal independente de plataforma que a Unreal Engine usa para renderizar belas imagens em 4K. As imagens são fornecidas por uma fração do custo, amortizando alguns dos dispendiosos cálculos de renderização em vários quadros.
Na cadeia de renderização, a TSR ocorre após a profundidade de campo e tudo o que se segue é aprimorado, como desfoque de movimento, bloom e assim por diante.
Depois que esses efeitos são aplicados ao GBuffer, a Unreal Engine renderiza a imagem final na tela.
As etapas descritas acima geram um quadro único na tela. Essas etapas geralmente são repetidas de 30 a 60 vezes por segundo, dependendo da taxa de quadros desejada para o jogo.
Equivalente no Unity
O Unity conta com soluções de pós-processamento baseadas no pipeline de renderização escolhido. Muitos dos efeitos disponíveis são semelhantes aos disponíveis na Unreal Engine.
Além disso, o Unity 6 também inclui o Spatial-Temporal Post-processing (STP), um dimensionador nativo baseado em software que usa técnicas de aumento de resolução espacial e temporal para produzir uma imagem de alta qualidade e sem serrilhado.
Visão geral das funcionalidades de renderização na Unreal Engine
Agora que você entende as etapas que a Unreal Engine realiza para renderizar um quadro na tela, já pode aprender mais sobre as funcionalidades específicas de renderização disponíveis na engine.
Para saber mais sobre as funcionalidades de renderização da Unreal Engine, leia o documento Lighting the Environment (Como iluminar o ambiente).