Renderização de jogos em 3D: introdução


Você joga o novo Call of Mario: Deathduty Battleyard no seu PC para jogos perfeito. Veja o lindo monitor 4K super amplo, admirando a paisagem magnífica e os detalhes complexos. Você já se perguntou como os gráficos chegam à tela? Você já se perguntou como o jogo faz o computador mostrar tudo isso?

Bem-vindo ao nosso tour pela renderização de jogos em 3D: uma jornada para iniciantes, a partir da qual você aprenderá a criar um quadro básico na tela.



Todos os anos, centenas de novos jogos para smartphones, consoles e PCs são lançados. A variedade de formatos e gêneros é muito grande, mas um deles, talvez, é dominado melhor - são jogos em 3D. Qual jogo foi o primeiro - uma pergunta discutível e uma rápida olhada na base do Guinness Book of Records deu vários resultados. Pode ser considerado o primeiro jogo Ultimate Knight Lore, lançado em 1984, mas, estritamente falando, as imagens neste jogo eram bidimensionais - nenhuma informação foi usada em três dimensões completas.

Portanto, se realmente queremos entender como os jogos 3D modernos formam uma imagem, precisamos começar com outro exemplo: Winning RunNamco, lançado em 1988. Talvez este tenha sido o primeiro jogo completamente tridimensional usando tecnologias que não são muito diferentes das modernas. É claro que qualquer jogo com idade superior a 30 anos não é o mesmo que, digamos, o Codemasters F1, lançado em 2018. Mas os diagramas de circuitos são semelhantes.


Neste artigo, veremos o processo de geração de uma imagem básica para um monitor ou TV usando um jogo em 3D. Vamos começar com o resultado final e nos perguntar: "O que estou vendo?"

Então analisamos cada estágio da formação da imagem que vemos. No curso da ação, consideraremos conceitos como vértices e pixels, texturas e passes, buffers e sombreamento, software e instruções. Vamos descobrir como a placa de vídeo está envolvida no processo e por que ela é necessária. Depois disso, você poderá ver seus jogos e PCs sob uma nova luz e começar a apreciar mais os gráficos de vídeo.

Opções de quadro: pixels e cores


Vamos começar o jogo em 3D. Como exemplo, levaremos a empresa Crytek lançada em 2007 Crysis . Abaixo está uma fotografia da tela em que o jogo é exibido:


Essa imagem geralmente é chamada de quadro . Mas o que exatamente estamos olhando? Usamos uma lente macro:


Infelizmente, o brilho e a luz de fundo externa do monitor estragam a foto, mas se a melhorarmos um pouco, teremos


Vemos que o quadro no monitor consiste em uma grade de elementos coloridos separados e, se os aumentarmos ainda mais, perceberemos que cada elemento é um bloco de três peças. Esse bloco é chamado de pixel (pixel, abreviação de elemento de imagem). Na maioria dos monitores, os pixels são pintados usando três cores: vermelho, verde e azul (RGB, vermelho-verde-azul). Para exibir um novo quadro, você precisa processar uma lista de milhares, se não milhões, de valores RGB e salvá-los em um pedaço de memória ao qual o monitor tem acesso. Esses fragmentos de memória são chamados de buffers , ou seja, o monitor recebe o conteúdo do buffer de quadro .

Este é o ponto final de todo o processo, então agora vamos avançar na direção oposta ao seu início. O processo é frequentemente descrito pelo termo renderização (renderização), mas, na realidade, é uma sequência de etapas separadas conectadas entre si, que em essência diferem bastante. Imagine que você é um chef em um restaurante com estrela Michelin: o resultado final é um prato de comida deliciosa, mas quanto precisa ser feito para isso. Assim como na culinária, são necessários ingredientes básicos para a renderização.

Elementos de construção necessários: modelos e texturas


Os principais componentes de qualquer jogo em 3D são recursos visuais que preenchem o mundo a ser desenhado. Filmes, programas de TV e teatros precisam de atores, figurinos, adereços, cenário, iluminação - a lista é bastante longa. A mesma coisa com os jogos em 3D. Tudo o que você vê no quadro gerado foi desenvolvido por artistas e especialistas em modelagem. Para deixar mais claro, vamos voltar para a velha escola e dar uma olhada no modelo Quake II da empresa de software id:


O jogo saiu há mais de 20 anos. Naquela época, o Quake II era uma obra-prima tecnológica, embora, como em qualquer jogo daqueles anos, os modelos pareçam muito primitivos. Mas é fácil demonstrar em que consistem.


Na foto anterior, vemos um cara angular composto por triângulos conectados um ao outro. Cada canto é chamado de vértice ou vértice. Cada vértice atua como um ponto no espaço e é descrito por pelo menos três números: as coordenadas x, y, z . No entanto, isso não é suficiente para um jogo em 3D; portanto, cada vértice possui valores adicionais: cor, direção da frente (sim, um ponto não pode ter uma frente ... basta ler!), Brilho, grau de transparência, etc.


Os vértices sempre têm um conjunto de valores associados aos mapas de textura. Estas são fotos das “roupas” usadas pela modelo. Mas como a imagem é plana, o mapa deve conter uma vista de qualquer direção, de onde podemos olhar para o modelo. O exemplo do Quake II ilustra uma abordagem simples: imagens da frente, de trás e dos lados (braços). E os modernos jogos em 3D já operam para modelos com vários mapas de texturas, cada um com muitos detalhes, sem espaços vazios entre eles. Alguns mapas não se parecem com materiais ou propriedades; em vez disso, fornecem informações sobre como a luz é refletida na superfície. Cada vértice possui um conjunto de coordenadas no mapa de textura associado ao modelo, para que possa ser "esticado" sobre o vértice. Isso significa que quando você move o vértice, a textura se move com ele.

No mundo tridimensional renderizado, tudo o que você vê começa com um conjunto de vértices e mapas de textura. Eles são carregados nos buffers de memória conectados um ao outro. O buffer de vértice ( buffer de vértice) contém texturas e partes de memória alocadas para renderização subsequente. O buffer de comando contém uma lista de instruções sobre o que fazer com esses recursos.

Tudo isso forma a estrutura necessária que será usada para criar a grade final de pixels coloridos. Em alguns jogos, essa é uma quantidade enorme de dados, porque leva muito tempo para recriar os buffers para cada novo quadro. Os jogos também armazenam em buffers as informações necessárias para formar um mundo inteiro que um jogador pode ver, ou uma parte grande o suficiente, atualizando conforme necessário. Por exemplo, em um jogo de corrida como F1 2018, tudo será armazenado em uma grande coleção de buffers. E em um jogo de mundo aberto como o Skyrim, os dados serão carregados nos buffers e excluídos deles à medida que a câmera se move ao redor do mundo.

Configuração de cena: vértices


Tendo todas as informações visuais, o jogo começará o processo de exibição visual. A cena começa em uma determinada posição por padrão, com o arranjo básico de modelos, fontes de luz etc. Essa será a tela "zero" - o ponto de partida para os gráficos. Geralmente não é exibido, é simplesmente processado pelo sistema. Para ilustrar o que acontece na primeira etapa da renderização, usaremos a ferramenta online Real-Time Rendering . Vamos criar o "jogo" mais simples: uma caixa no chão.


Este objeto contém 8 vértices, cada um dos quais é descrito por uma lista de números. Os vértices formam um modelo composto por 12 triângulos. Cada triângulo, e até o próprio objeto, é chamado de primitivo . À medida que as primitivas se movem, giram e escalam, os números passam por cadeias de operações matemáticas e são atualizados.


Observe que os números dos pontos do modelo não mudam. Esses números mostram exatamente onde o modelo está localizado no mundo virtual. A consideração dos cálculos matemáticos correspondentes está além do escopo do artigo; diremos apenas que primeiro todos os objetos são colocados onde deveriam estar. E então a coloração começa.


Pegue outro modelo que tenha 10 vezes mais vértices que a caixa anterior. No processo mais simples de pintura, a cor de cada vértice é obtida e, em seguida, as alterações de cor de superfície para superfície são calculadas. Isso é chamado de interpolação .


Aumentar o número de vértices no modelo não apenas permite criar objetos mais realistas, como também melhora o resultado da interpolação de cores.


Nesta fase da renderização, o efeito das fontes de luz na cena pode ser calculado em detalhes. Por exemplo, como os materiais do modelo refletem as cores. Esses cálculos devem levar em consideração a posição e a direção da câmera, bem como a posição e a direção das fontes de luz.


Para fazer isso, existem vários métodos matemáticos diferentes, alguns simples, outros muito complexos. Na ilustração acima, vemos que o objeto à direita parece muito melhor e mais realista, mas é necessário mais trabalho para desenhá-lo.

É importante notar que agora comparamos objetos com um pequeno número de picos com os jogos mais modernos. Role para cima e examine cuidadosamente a imagem de Crysis: essa cena exibe mais de um milhão de triângulos. Usando o benchmark Unigine Valley como exemplo, você pode entender quantos triângulos são usados ​​nos jogos modernos.


Cada objeto nesta imagem consiste em vértices conectados entre si, formados pelas primitivas que consistem em triângulos. A referência pode ser executada no modo de estrutura de arame, no qual as bordas de cada triângulo são indicadas por linhas brancas.


Como você pode ver, qualquer objeto consiste em triângulos e para cada triângulo são calculados o local, a direção e a cor. Isso leva em consideração a localização das fontes de luz, bem como a localização e a direção da câmera. Todas as alterações associadas aos vértices devem ser transferidas para o jogo, de modo a ter todas as informações necessárias para desenhar o próximo quadro - isso é feito atualizando o buffer de vértices.

Surpreendentemente, essa não é a parte mais difícil de renderizar, com o hardware certo, todos os cálculos são feitos em alguns milésimos de segundo! Ir em frente.

Perder dimensão: rasterização


Depois de processar todos os vértices e concluir a colocação de todos os objetos em nossa cena tridimensional, o processo de renderização prossegue para um estágio muito importante. Até o momento, o jogo era realmente tridimensional, mas o quadro final não é mais esse: no decorrer de uma série de mudanças, o mundo visto é transformado de um espaço 3D que consiste em milhares de pontos conectados em uma imagem bidimensional que consiste em pixels coloridos. Na maioria dos jogos, esse procedimento consiste em pelo menos duas fases: espaço na tela de projeção (projeção do espaço na tela) e rasterização .


De volta à nossa ferramenta de renderização baseada na Web, ela nos mostrará como o volume do mundo virtual se transforma em uma imagem plana. A câmera é mostrada à esquerda, as linhas que emanam dela criam uma pirâmide truncada de visibilidade (frustum) e tudo o que entra nela pode ser exibido no quadro final. A secção perpendicular da pirâmide é chamado o visor - isso é o que será mostrado no monitor. Inúmeros cálculos matemáticos são usados ​​para projetar todo o conteúdo da pirâmide na área de visualização, levando em consideração a perspectiva da câmera.

Embora os gráficos na área de visualização sejam bidimensionais, os dados ainda são verdadeiramente tridimensionais e, posteriormente, essas informações serão usadas para calcular quais primitivas são visíveis para nós e quais estão ocultas. Isso pode ser surpreendentemente difícil de fazer, porque os primitivos podem projetar sombras visíveis para nós, mesmo que os próprios primitivos estejam ocultos de nós. Remova escondido de nós as primitivas chamadas de abandono (abate). Esta operação pode afetar significativamente a velocidade de renderização de todo o quadro. Depois que a classificação em primitivas visíveis e ocultas é concluída, e os triângulos são removidos fora dos limites da pirâmide de visibilidade, o último estágio da tridimensionalidade é concluído e o quadro é totalmente bidimensional usando rasterização.


A ilustração acima mostra um exemplo muito simples de quadro que contém um primitivo. A grade de pixels é sobreposta à forma geométrica e os pixels correspondentes são marcados para processamento subseqüente. O resultado final não se parece muito com o triângulo original, porque não estamos usando pixels suficientes. Nesse sentido, surge o problema de serrilhado (serrilhado, trampolim), que é resolvido de várias maneiras. Portanto, uma alteração na resolução do jogo (o número total de pixels no quadro) afeta tanto o resultado final: mais pixels não apenas melhoram a exibição dos formulários, mas também reduzem o efeito de aliasing indesejado.

Depois de concluir esta parte da renderização, passamos para o próximo grande passo: a coloração final de todos os pixels no quadro.

Leve a luz: estágio de pixel


Chegamos ao estágio mais difícil de renderização. Uma vez se tratou de usar modelos de roupas (texturas) usando informações de pixel (originalmente obtidas dos vértices). No entanto, o fato é que, embora as texturas e o próprio quadro sejam bidimensionais, o mundo virtual no estágio de processamento de vértices foi distorcido, alterado e alterado. Para explicar tudo isso, cálculos matemáticos adicionais são usados, no entanto, novos problemas podem ser característicos do resultado.


Nesta ilustração, uma textura quadriculado é aplicada ao plano. Há uma ondulação visual desagradável, que é exacerbada pelo alias. Para resolver esse problema, use versões menores de mapas de textura ( vários mapeamentos , mipmaps), reutilize as informações dessas texturas ( filtragem , filtragem) e cálculos matemáticos adicionais. O efeito é perceptível:


Para qualquer jogo, esse foi realmente um estágio difícil, mas hoje não é mais, porque, devido ao amplo uso de outros efeitos visuais, como reflexos e sombras, o processamento de textura se tornou um estágio relativamente pequeno do processo de renderização. Ao reproduzir em altas resoluções, a carga nos estágios de rasterização e processamento de pixels aumenta, mas isso afeta o processamento de vértices relativamente pouco. Embora a coloração primária devida às fontes de luz seja realizada no estágio de vértice, efeitos de iluminação mais sofisticados podem ser aplicados.


Na ilustração anterior, não vemos mais mudanças de cores entre triângulos diferentes, o que nos dá a sensação de um objeto sem costura suave. Embora neste exemplo a esfera consista no mesmo número de triângulos que a esfera verde na ilustração acima, como resultado do procedimento de coloração de pixels, parece-nos que muito mais triângulos são usados.


Em muitos jogos, a fase do pixel precisa ser executada várias vezes. Por exemplo, para que um espelho ou superfície da água reflita o mundo circundante, primeiro é preciso desenhar esse mundo. Cada corrida é chamada de passe (passe) e, para obter a imagem final de cada quadro, pode ser usado facilmente quatro ou mais passes.

Além disso, às vezes você precisa executar o estágio de vértice novamente para redesenhar o mundo a partir de outro ponto e usar esta imagem em uma cena que é mostrada ao jogador. Para fazer isso, use a renderização de buffer único (destinos de renderização) - use buffers que atuam como o armazenamento final do quadro, mas também podem funcionar como texturas com uma passagem diferente.

Para avaliar a complexidade do estágio de pixel, você pode leranálise de quadros no Doom 2016 . Você ficará chocado com o número de operações necessárias para criar um quadro.


Todo o trabalho realizado para criar o quadro deve ser salvo no buffer, seja o resultado final ou intermediário. Em geral, o jogo usa em tempo real pelo menos dois buffers para exibição final: um para "trabalho atual" e o segundo buffer está aguardando o acesso de um monitor ou está em processo de exibição. Você sempre precisa de um buffer de tela no qual o resultado da renderização será salvo e, quando todos os buffers estiverem cheios, será necessário seguir em frente e criar um novo buffer. Após a conclusão do trabalho com o quadro, é dado um comando simples, os buffers finais do quadro são trocados, o monitor recebe o último quadro renderizado e o processo de renderização do próximo quadro é iniciado.


Nesse quadro da Assassin's Creed Odyssey , vemos o conteúdo do buffer de quadros completo. Este conteúdo pode ser representado em uma tabela que contém apenas números. Eles são enviados na forma de sinais elétricos para um monitor ou TV, e os pixels da tela alteram seus valores. Nossos olhos veem uma imagem plana e sólida, mas nosso cérebro a interpreta como tridimensional. Há tanto trabalho escondido nos bastidores de apenas uma cena do jogo que vale a pena dar uma olhada em como os programadores podem lidar com isso.

Gerenciamento de processos: APIs e instruções


Para descobrir como fazer o jogo executar e gerenciar todos os cálculos, vértices, texturas, iluminação, buffers, etc. - esta é uma tarefa enorme. Felizmente, ajude-nos nessas interfaces de programação (interface de programação de aplicativos, API).

As APIs de renderização reduzem a complexidade geral, oferecendo estruturas, regras e bibliotecas de software que permitem instruções simplificadas e independentes de hardware. Faça qualquer jogo 3D lançado para PC nos últimos três anos: ele foi criado usando uma das três APIs populares - Direct3D, OpenGL ou Vulkan. Existem outros desenvolvimentos semelhantes, especialmente no segmento móvel, mas neste artigo falaremos sobre os três mencionados.


Apesar das diferenças nos nomes de instruções e operações (por exemplo, um bloco de código para processamento de pixels no DirectX é chamado de sombreador de pixels e no Vulkan é chamado de sombreador de fragmentos), o resultado final não difere, mais precisamente, não deve ser diferente.

A diferença será manifestada em qual equipamento é usado para renderização. As instruções geradas pela API precisam ser convertidas em comandos compatíveis com o hardware que são processados ​​pelos drivers de dispositivo. E os fabricantes de equipamentos precisam gastar muitos recursos e tempo para que seus drivers realizem essa conversão o mais rápido e corretamente possível.


Por exemplo, a versão beta inicial de The Talos Principle (2014) suportava todas as três APIs mencionadas. Para demonstrar como os resultados de diferentes combinações de drivers e interfaces podem diferir, executamos o benchmark interno padrão, definindo a resolução para 1080p e as configurações de qualidade máxima. O processador Intel Core i7-9700K funcionou sem overclock, a placa de vídeo Nvidia Titan X (Pascal), RAM - DDR4 de 32 GB RAM.

  • DirectX 9 = uma média de 188,4 quadros / s.
  • DirectX 11 = uma média de 202,3 quadros / s.
  • OpenGL = média de 87,9 quadros / s.
  • Vulkan = uma média de 189,4 quadros / s.

Não analisaremos os resultados e eles certamente não dizem que uma API é "melhor" que a outra (não se esqueça, a versão beta do jogo foi testada). Diremos apenas que a programação para diferentes APIs está associada a várias dificuldades e, a qualquer momento, o desempenho também será diferente. Em geral, os desenvolvedores de jogos escolhem a API com a qual têm mais experiência e otimizam seu código. Às vezes, o termo mecanismo é usado para descrever o código responsável pela renderização, mas estritamente falando, o mecanismo é um conjunto completo de ferramentas que processa todos os aspectos do jogo, não apenas gráficos.

Não é tão fácil criar um programa a partir do zero que renderiza um jogo em 3D. Portanto, hoje em muitos jogos são utilizados sistemas de terceiros licenciados (por exemplo, Unreal Engine) Para avaliar sua complexidade, abra o mecanismo de código aberto do Quake e visualize o arquivo gl_draw.c: ele contém instruções para diferentes operações de renderização e reflete apenas uma pequena parte do mecanismo inteiro. Mas o Quake foi lançado há mais de 20 anos e todo o jogo (incluindo todos os recursos visuais, sons, músicas etc.) ocupa 55 MB. Para comparação, no Far Cry 5, apenas shaders ocupam 62 MB.

O tempo é mais importante: usar o equipamento certo


Todos os itens acima podem ser calculados e processados ​​pelo processador de qualquer sistema de computador. Os processadores modernos da família x86-64 suportam todas as operações matemáticas necessárias e até contêm subsistemas separados para isso. No entanto, a tarefa de renderizar um único quadro requer numerosos cálculos repetidos e paralelização significativa do trabalho. Os processadores centrais não estão adaptados para isso, porque são criados para resolver a maior variedade possível de tarefas. Processadores especializados para computação gráfica são chamados de GPUs (unidades de processamento gráfico). Eles são criados para DirectX, OpenGL e Vulkan.

Usaremos uma referência que permite renderizar um quadro usando um processador central ou equipamento especializado - V-ray NEXTEmpresas do Grupo Caos. De fato, ele faz o traçado de raios em vez de renderizar, mas a maioria das operações numéricas aqui também depende do hardware.


Vamos passar a referência em três modos: apenas o processador central, apenas o processador gráfico e uma combinação dos dois processadores:

  • CPU sozinha = 53 milhões de raios
  • GPU sozinha = 251 milhões de raios
  • A combinação dos dois processadores = 299 milhões de raios

A unidade de medida pode ser ignorada, a essência é cinco vezes a diferença. Mas, ainda assim, esse teste não está muito relacionado aos jogos, então vamos ao benchmark da velha escola Futuremark 3DMark03 . Vamos executar um teste simples de Wings of Fury com um cálculo forçado de todos os shaders de vértice (ou seja, com um conjunto completo de operações para mover e colorir triângulos) usando o processador central.


O resultado não deve surpreendê-lo:

  • Processador central = média de 77 quadros / s.
  • GPU = média de 1.580 quadros / s.

Quando todos os cálculos com os vértices são executados pelo processador central, são necessários 13 ms em média para renderizar e exibir cada quadro. E ao usar um processador gráfico, esse número cai para 0,6 ms - mais de 20 vezes mais rápido.

A diferença aumenta ainda mais se você executar o teste de benchmark mais difícil - a Mãe Natureza. O processador central produziu 3,1 quadros / s insignificantes! E o processador gráfico disparou em 1388 quadros / s .: quase 450 vezes mais rápido. Observe: o 3DMark03 saiu 16 de volta e, no teste no processador central, apenas os vértices são processados, o processador gráfico ainda assume o estágio de rasterização e pixel. Imagine se o benchmark fosse moderno e a maioria das operações fosse executada programaticamente?


Agora, tentaremos novamente o benchmark Unigine Valley , os gráficos que ele processa são muito semelhantes aos usados ​​em jogos como Far Cry 5. Também há um mecanismo de renderização de software completo, além do DirectX 11. padrão. Ao rodar em um processador de vídeo, obtivemos um resultado médio de 196 quadros / s . E a versão do software? Após algumas falhas, um poderoso PC de teste gerou uma média de 0,1 quadros / s. quase duas mil vezes mais devagar.

A razão para uma diferença tão grande está nos cálculos matemáticos e no formato de dados usado na renderização em 3D. Cada núcleo da CPU está equipado com módulos de ponto flutuante. O i7-9700K contém 8 núcleos, cada um com dois desses módulos. Embora a arquitetura dos módulos no Titan X seja diferente, ambos os tipos podem executar os mesmos cálculos com dados do mesmo formato. Esta placa de vídeo possui mais de 3500 módulos para realizar cálculos comparáveis ​​e, embora sua frequência de clock seja muito menor do que no processador central (1,5 GHz e 4,7 GHz), no entanto, o processador de vídeo aceita o número de módulos.

Embora o Titan X não seja uma placa gráfica de massa, mesmo um modelo econômico ultrapassará qualquer processador central. Portanto, todos os jogos 3D e APIs são projetados para equipamentos especializados. Você pode fazer o download de V-ray , 3DMark ou qualquer benchmark Unigine e teste seu sistema - veja você mesmo como os processadores de vídeo estão adaptados para renderizar gráficos em jogos.

Palavras finais


Foi uma pequena digressão no processo de criação de um único quadro em jogos 3D, de um ponto no espaço a uma imagem colorida em um monitor.

De fato, todo o processo está apenas trabalhando com números. No entanto, muito permaneceu fora do escopo do artigo. Não consideramos cálculos matemáticos específicos da álgebra linear euclidiana, trigonometria e cálculos diferenciais realizados por vertex e pixel shaders. Também não falamos sobre como as texturas são processadas usando amostragem estatística. Omiti efeitos visuais interessantes, como bloquear a luz ambiente no espaço da tela, reduzir a interferência no rastreamento de raios, usar a faixa dinâmica estendida e a suavização temporal.

E da próxima vez que você lançar um jogo 3D moderno, esperamos que você não apenas observe os gráficos com olhos diferentes, mas também queira saber mais sobre ele.

All Articles