Na primeira parte, onde daremos uma olhada mais aprofundada na criação de jogos 3D, nos concentraremos inteiramente no estágio superior do processo. Isso significa arrastar nossos livros de matemática, escovar em álgebra linear, matrizes e trigonometria - oh sim!

Faremos como os modelos 3D são transformados e como as fontes de luz são calculadas. As diferenças entre os shaders de canto e geometria serão investigadas minuciosamente e você verá onde o mosaico se encaixa. Para ajudar nas explicações, usaremos diagramas e exemplos de código para mostrar como a matemática e os números são tratados em um jogo. Se você não estiver pronto para tudo isso, não se preocupe - você pode começar. Renderização de jogos 3D 101. No entanto, uma vez definido, continue lendo para nossa primeira visão do mundo dos gráficos 3D.

Qual é o propósito?

No mundo da matemática, um ponto é simplesmente uma posição dentro de um campo geométrico. Não há nada menor do que um ponto, pois não tem tamanho, então eles podem ser usados ​​para definir claramente onde objetos como linhas, planos e volumes começam e terminam.




Para gráficos 3D, esta informação é crucial para determinar como tudo ficará, já que tudo exibido é uma linha, um plano, etc. Coleção. A imagem abaixo é uma captura de tela do lançamento de 2015 da Bethesda. Fallout 4:




Pode ser um pouco difícil ver como esses são apenas uma grande pilha de pontos e linhas, então vamos mostrar como a mesma cena fica no modo 'wireframe'. O mecanismo de renderização 3D ignora as texturas e efeitos feitos no estágio de pixel e desenha nada além de linhas coloridas conectando os pontos.







Tudo parece muito diferente agora, mas podemos ver vários objetos, o ambiente e todas as linhas que se juntam para criar o fundo. Alguns são apenas um punhado de linhas, como pedras no primeiro plano, enquanto outros têm linhas suficientes para parecerem sólidas.

Cada ponto no início e no final de cada linha foi trabalhado com muita matemática. Alguns desses cálculos são muito rápidos e fáceis; outros são muito mais difíceis. Há ganhos de desempenho significativos trabalhando juntos em grupos de pontos, especialmente em forma de triângulo, portanto, vamos dar uma olhada nisso.




O que é necessário para um triângulo?

Nome triângulo nos diz que a forma tem 3 ângulos internos; Para isso, precisamos de 3 cantos e 3 linhas conectando os cantos. Nome correto do canto Colina (vértices são a palavra no plural) e cada um é definido por um ponto. Uma vez que somos baseados em um mundo geométrico 3D, Sistema de coordenada cartesiana para pontos. Isso geralmente é escrito junto com 3 valores, por exemplo (1, 8, -3) ou mais geralmente (X ve Z).

A partir daqui, podemos adicionar mais dois vértices para obter um triângulo:




Observe que as linhas mostradas não são realmente necessárias - podemos apenas ter pontos e podemos dizer ao sistema que esses 3 vértices formam um triângulo. Todos os dados do vértice são armazenados em um bloco contíguo de memória chamado de pára-choque de canto; As informações sobre a forma que eles farão são codificadas diretamente no programa de renderização ou armazenadas em outro bloco de memória. buffer de diretório.

No caso do primeiro, diferentes formas que podem ser formadas a partir dos cantos são chamadas primitivo O Direct3D apresenta lista, faixas e leques em forma de ponto, linha e triângulo. Usadas corretamente, as listras triangulares ajudam a melhorar o desempenho usando cantos para triângulos múltiplos. No exemplo abaixo, podemos ver que apenas 4 cantos são necessários para unir 2 triângulos - se eles fossem separados, precisaríamos de 6 vértices.

Se você deseja cobrir uma coleção maior de cantos, por exemplo. um modelo NPC no jogo, então um - este é outro bloco de memória, mas consiste em vários buffers (vértice, índice, etc.) e recursos de textura para o modelo. A Microsoft fornece uma introdução rápida a esses usos de buffer. documentos online fonte.

Por enquanto, vamos nos concentrar no que é feito nesses cantos em um jogo 3D, sempre que um novo quadro é criado (se você não tiver certeza do que isso significa, faça uma varredura rápida novamente) render 101) Simplificando, uma ou duas coisas são feitas para eles:

  • Mova o vértice para um novo local
  • Mudar a cor do canto

Pronto para alguma matemática? Boa! Porque é assim que se faz.

Insira o vetor

Imagine que há um triângulo na tela e você pressiona um botão para movê-lo para a esquerda. NaturalmenteX ve Z) os números de cada vértice variam de acordo e eles são; mas, Quão Isso pode parecer um pouco incomum. A grande maioria dos sistemas de renderização de gráficos 3D, em vez de alterar as coordenadas, usa uma ferramenta matemática específica para fazer o trabalho: vetores.

Um vetor pode ser visto como uma seta que aponta para um lugar específico no espaço e pode ter qualquer comprimento necessário. Os cantos são na verdade descritos usando vetores baseados em coordenadas cartesianas desta forma:

Observe como a seta azul começa em um local (neste caso, Origem) e se estende até o pico. Usamos o que foi chamado cnotação de coluna para descrever este vetor remo a notação também funciona. Você também notará que há mais um valor extra - o quarto número é geralmente com componente w e é usado para indicar se o vetor é usado para descrever a posição de um vértice ( posição do vetor) ou um aspecto geral (a noite vetor). No caso do último, será semelhante a este:

Este vetor aponta na mesma direção e tem o mesmo comprimento que o vetor de posição anterior, então (X ve Z) os valores serão os mesmos; Mas w-vetores de componentes são zero em vez de 1. O uso de vetores de direção será esclarecido posteriormente neste artigo, mas por enquanto vamos considerar o fato de que todos os pontos de canto na cena 3D serão definidos dessa forma. Por quê? Porque com este formato, é muito mais fácil começar a movê-los.

Matemática, matemática e mais matemática

Lembre-se de que temos um triângulo básico e queremos movê-lo para a esquerda. Cada vértice é definido por um vetor de posição, então precisamos fazer a 'matemática móvel' ( conversões) tem que trabalhar nesses vetores. Insira o próximo veículo: Series (ou mãe para um destes). Esta é uma série de valores escritos como linhas e colunas, um pouco como uma planilha do Excel.

Para cada tipo de transformação que queremos fazer, existe uma matriz associada para acompanhá-la, e é apenas o caso de multiplicar a matriz de transformação e o vetor posição juntos. Não examinamos os detalhes específicos de como e por que isso acontece, mas podemos ver como é.

Mover vértice no espaço 3D tradução e o cálculo necessário é:

x0etc. os valores representam as coordenadas originais do vértice; delta-x Os valores indicam quanto o pico deve se mover. O cálculo do vetor de matriz resulta na simples adição dos dois ( w o componente permanece intocado, então a resposta final ainda é um vetor de posição).

Além de mover as coisas, podemos querer girar o triângulo ou dimensioná-lo para um tamanho maior ou menor - há transformações para ambos.

Ferramenta gráfica baseada em WebGL Site de renderização em tempo real para visualizar esses cálculos em uma figura inteira. Vamos começar com um cubóide em um local padrão:

Nesta ferramenta online, o ponto do modelo refere-se ao vetor posição, a matriz terrestre é a matriz de transformação e o ponto espacial terrestre é o vetor posição do vértice transformado.

Agora vamos aplicar várias transformações ao cubóide:

Na imagem acima, a figura tradução 5 unidades em cada direção. Podemos ver esses valores na grande matriz do meio, a última coluna. O vetor de posição original (4, 5, 3, 1) permanece como deveria, mas o vértice transformado agora é traduzido como (9, 10, 8, 1).

Nessa transformação, tudo foi escalado duas vezes: o cubóide agora tem lados com o dobro do comprimento. O último exemplo a ser examinado é um ponto de inflexão:

O cuboide é girado em um ângulo de 45 °, mas a matriz seus ve cosseno ângulo. Uma verificação rápida em qualquer calculadora científica sem (45 °) = 0,7071 ... arredonda para o valor 0,71 mostrado. Nós recebemos a mesma resposta cosseno valor.

Matrizes e vetores não precisam ser usados; uma alternativa comum para lidar com rotações particularmente complexas, números complexos e Kuaterniyonlar. Essa matemática é um grande avanço em relação aos vetores, então vamos passar por transformações.

O poder do shader de pico

Neste estágio, devemos considerar que tudo isso deve ser resolvido pelas pessoas que programam o código de construção. Se um desenvolvedor de jogos estiver usando um mecanismo de terceiros (como Unity ou Unreal), isso já será feito para eles, mas qualquer pessoa que fizer seus próprios cálculos do zero precisará descobrir quais cantos precisam fazer.

Então, como isso se parece em termos de código?

Usaremos exemplos do excelente site para ajudar com isso. Braynzar Soft. É um ótimo lugar para aprender o básico e coisas mais avançadas se você quiser começar a programar 3D sozinho.

Este exemplo é a transformação 'tudo em um'. Cria as matrizes de transformação correspondentes com base em uma entrada do teclado e as aplica ao vetor de posição original em uma única operação. Lembre-se de que isso sempre é feito em uma determinada ordem (escala - girar - inverter) porque qualquer outra forma estragará completamente o resultado.

Esses tipos de blocos de código são chamados Hill Shaders e podem variar muito, dependendo do que fazem, de seu tamanho e complexidade. O exemplo acima é tão simples e inegável quanto parece um shader porque não utiliza a natureza totalmente programável dos shaders. Um conjunto mais complexo de sombreadores pode transformá-lo em espaço 3D, calcular como a cena aparecerá em sua câmera e, em seguida, transferir esses dados para o próximo estágio do processo de renderização. Veremos mais exemplos à medida que passarmos pela sequência de apara de canto.

Claro que eles podem ser usados ​​para muito mais, e lembre-se que cada vez que você joga um jogo em 3D, todos os movimentos que você pode ver são feitos pelo processador gráfico, seguindo as instruções nos shaders de canto.

No entanto, nem sempre foi assim. Voltando ao final da década de 1990, as placas gráficas daquela época não tinham a capacidade de lidar com cantos e primitivos, tudo feito inteiramente na CPU.

Um dos primeiros processadores a fornecer aceleração de hardware dedicada para este tipo de processamento A GeForce original da Nvidia foi lançada em 2000 e esta propriedade foi marcada Conversão de Hardware e Iluminação (ou Hardware TnL para breve). As operações que este hardware podia controlar eram muito rígidas e constantes em termos de comandos, mas isso mudou rapidamente conforme novos chips gráficos foram lançados. Hoje, não há hardware separado para processamento de canto e as mesmas unidades lidam com tudo: pontos, primitivos, pixels, texturas, etc.

Falando sobre iluminação, deve-se observar que tudo o que vemos é obviamente causado pela luz, então vamos ver como isso pode ser tratado no estágio de pico. Para fazer isso, usaremos algo que mencionamos anteriormente neste artigo.

Luzes do motor da câmera!

Imagine esta cena: o ator está em uma sala escura iluminada por uma única fonte de luz à direita. No meio da sala está um bule de chá gigante flutuante. Ok, provavelmente precisaremos de um pouco de ajuda para visualizar isso, Site de renderização em tempo real, para ver algo assim em ação:

Agora observe que este objeto é uma coleção de triângulos retos costurados juntos; Isso significa que o plano de cada triângulo apontará para uma determinada direção. Alguns estão olhando para a câmera, outros para o outro lado, enquanto outros estão tortos. A luz da fonte atinge cada plano e salta em um determinado ângulo.

Dependendo de para onde a luz está indo, a cor e o brilho do avião mudarão, e tudo isso precisa ser calculado e calculado para garantir que a cor do objeto pareça correta.

Para começar, precisamos saber para qual direção o avião está voltado e para isso vetor normal avião. Esta é outra seta, mas ao contrário do vetor de posição, o tamanho não importa (na verdade, eles sempre são redimensionados após o cálculo, então têm exatamente 1 unidade de comprimento) e sempre dik plano (ângulo reto).

A normal do plano de cada triângulo, o produto vetorial dos dois vetores de direção (p ve q Mostrado acima) formam os lados do triângulo. Na verdade, é melhor trabalhar para cada vértice e não para cada triângulo, mas considerando que sempre será mais que o anterior em relação ao último, é mais rápido fazê-lo apenas para os triângulos.

Depois de atingir o normal de uma superfície, você pode começar a levar em consideração a fonte de luz e a câmera. As luzes podem ser de diferentes tipos na renderização 3D, mas apenas para os fins deste artigo direcional luzes, por exemplo. um holofote. Como o plano de um triângulo, o holofote e a câmera apontarão para uma determinada direção, talvez algo assim:

O vetor de luz e o vetor normal podem ser usados ​​para encontrar o ângulo em que a luz atinge a superfície (usando a relação entre o produto escalar dos vetores e o produto de suas dimensões). Os vértices do triângulo trarão informações adicionais sobre suas cores e materiais - neste último caso, explica o que acontece com a luz quando atinge a superfície.

Uma superfície lisa e metálica refletirá quase toda a luz que entra no ângulo de onde vem e dificilmente mudará de cor. Em contraste, um material áspero e fosco espalha a luz de uma forma menos previsível e muda de cor sutilmente. Para levar isso em consideração, os pontos de canto devem ter valores adicionais:

  • Base de cor original
  • Propriedade do material ambiente - um valor que determina quanta luz de 'fundo' o vértice pode absorver e refletir
  • Propriedade do material difundido - outro valor, mas desta vez mostrando quão grosso é o pico, isso afeta o quanto a luz espalhada é absorvida e refletida
  • Propriedades especulativas do material - dois valores que fornecem uma medida de quão 'brilhante' o pico é

Diferentes modelos de iluminação usam várias fórmulas matemáticas para agrupá-los, e o cálculo produz um vetor para a luz incidente. Isso é combinado com o vetor da câmera, a aparência geral do triângulo pode ser determinada.

Examinamos os detalhes mais sutis aqui e por um motivo melhor: pegue qualquer livro sobre renderização 3D e você verá todos os capítulos dedicados a este único processo. No entanto, os jogos modernos geralmente realizam a maioria dos cálculos de iluminação e efeitos materiais no estágio de computação de pixel, portanto, abordaremos esse tópico novamente em outro artigo.

Tudo o que cobrimos até agora foi feito usando hill shaders e pode parecer que não há quase nada que eles não possam fazer; Infelizmente existe. Os sombreadores de canto não podem criar novos pontos de canto e cada sombreador deve trabalhar em cada vértice. Teria sido útil se houvesse uma maneira de usar um pouco de código para criar mais triângulos, uma maneira de ter um shader funcionando entre o que já temos (para melhorar a qualidade visual) e sobretudo primitivo (para agilizar as coisas). até). Com processadores gráficos modernos, lata Faça isso!

Por favor, senhor, quero mais (triângulos)

Os chips gráficos mais recentes são extremamente poderosos, capazes de milhões de cálculos de vetores de matriz a cada segundo; eles podem facilmente passar por cima de uma grande pilha de cantos em nenhum momento. Por outro lado, criar modelos altamente detalhados consome muito tempo e, se o modelo ficar um pouco fora de cena, todos os detalhes extras serão desperdiçados.

O que precisamos é uma maneira de dizer ao processador para dividi-lo em um primitivo maior, uma coleção de triângulos menores que estão todos conectados dentro do maior original, como o único triângulo reto que estamos olhando. O nome deste processo: tesselação e os chips gráficos já fazem isso há um bom tempo; O que melhorou ao longo dos anos é a quantidade de controle que os programadores têm sobre a operação.

Para ver isso em ação, usaremos: A ferramenta de benchmark Paradise da Unigine, uma vez que nos permite aplicar diferentes quantidades de mosaico aos modelos específicos usados ​​no teste.

Para começar, vamos pegar um lugar no benchmark e examiná-lo sem o mosaico aplicado. Tenha cuidado para que os paralelepípedos no chão pareçam muito falsos - a textura usada é eficaz, mas não parece certa. Vamos aplicar alguns mosaicos à cena; O Unigine aplica o motor apenas em algumas partes, mas a diferença é dramática.

O piso, as bordas do prédio e a porta agora parecem muito mais realistas. Se executarmos o processo novamente, podemos ver como isso é feito, mas desta vez com as bordas das primitivas destacadas (modo wireframe):

Podemos ver claramente porque o chão parece tão estranho - completamente plano! A porta também está alinhada com as paredes e as bordas do edifício nada mais são do que simples cubos.

No Direct3D, os elementos primitivos podem ser divididos em um grupo menor de partes ( compartimento inferior) Executando uma sequência de 3 estágios. Primeiro, programadores shader de corpo - basicamente, este código é um patch de geometria. Imagine isso como um mapa que informa ao processador onde os novos princípios e linhas aparecerão dentro do primitivo inicial.

Em seguida, a unidade tesselator dentro do processador gráfico aplica o patch ao princípio. Finalmente, shader de área calcula as posições de todos os novos cantos é executado. Esses dados podem ser alimentados de volta no buffer de vértices conforme necessário para que os cálculos de iluminação possam ser feitos novamente, mas desta vez com melhores resultados.

Então, como é isso? Vamos disparar a versão wireframe da cena do mosaico:

A verdade é que definimos o nível de mosaico em um nível bastante extremo para ajudar a explicar o processo. Tão bom quanto os chips gráficos modernos, não é algo que você gostaria de fazer em todos os jogos - por exemplo, coloque o poste de luz perto da porta.

Nas imagens sem estrutura de arame, você é obrigado a descrever a diferença nessa distância e pode ver esse nível de mosaico agrupado em muitos triângulos, alguns dos quais são difíceis de dizer. Ainda assim, é usado corretamente e esta função de renderização de canto pode resultar em alguns excelentes efeitos visuais, especialmente ao tentar simular colisões de corpos macios.

Nas imagens sem estrutura de arame, você é obrigado a descrever a diferença nessa distância e pode ver esse nível de mosaico agrupado em muitos triângulos, alguns dos quais são difíceis de dizer. Vamos dar uma olhada em como isso pode ser em termos de código Direct3D; Para fazer isso, usaremos um exemplo de outro grande site. RasterTek.

Aqui, um único triângulo verde é dividido em muitos outros triângulos bebês.

A coroação é feita com 3 shaders separados (ver fig. amostra de código): um shader de vértice para definir o triângulo pronto para triangulação, um shader de corpo para criar o patch e um shader de domínio para renderizar novos cantos. O resultado é muito simples, mas o exemplo da Unigine destaca os benefícios e perigos potenciais do uso de mosaicos em qualquer lugar. Ainda assim, é usado corretamente e esta função de renderização de canto pode resultar em alguns excelentes efeitos visuais, especialmente ao tentar simular colisões de corpos macios.

Você pode lidar com isso, capitão!

Lembra dos corner shaders e de estar sempre rodando em todos os cantos da cena? Não é difícil ver como o mosaico pode tornar isso um problema real. E há muitos efeitos visuais que você deseja lidar com várias versões do mesmo primitivo, mas não deseja criar um grande número no início; cabelos, pelos, grama e partículas explosivas são bons exemplos disso.

Felizmente, existe outro sombreador disponível apenas para esse tipo de coisa - shader de geometria. É uma versão mais restritiva do sombreador de vértice, mas pode ser aplicada a um princípio inteiro e combinada com o mosaico dá aos programadores maior controle sobre grandes grupos de vértices.

O Direct3D, como todas as APIs gráficas modernas, permite uma ampla variedade de cálculos em pontos críticos. Os dados finalizados podem ser enviados para a próxima fase do processo de criação (pixelização) ou realimentado no pool de memória para que possa ser retrabalhado ou lido pela CPU para outros fins. Isso pode ser feito como um fluxo de dados, como destaca a Microsoft Documentação Direct3D:

saída de fluxo a cena é particularmente útil para efeitos com muitas partículas em todos os lugares, pois pode realimentar todos os princípios (não cantos individuais) no loop de renderização. O mesmo truque pode ser alterado ou dinâmico amortecedor de canto, mas é melhor manter os amortecedores de entrada estáveis ​​porque há um impacto no desempenho se eles precisarem 'abrir' para mudar.

A renderização de cantos é uma parte crítica da renderização, pois determina como a cena é organizada da perspectiva da câmera. Os jogos modernos podem usar milhões de triângulos para criar seus mundos, e cada um desses cantos será transformado e queimado de alguma forma.

Lidar com toda essa matemática e dados pode parecer um pesadelo logístico, mas os processadores gráficos (GPUs) e APIs são projetados com tudo isso em mente - retratando uma fábrica funcionando perfeitamente disparando um item por uma série de estágios de produção e fazendo isso bem. você vai entender.

Com experiência Criação de jogos 3D os programadores têm uma base abrangente em matemática e física avançadas; Eles usam todos os truques e ferramentas do comércio para otimizar as transações e reduzir a fase de processamento de pico para apenas alguns milissegundos. E isso é apenas o começo da criação de um quadro 3D - depois a etapa de rasterização e, em seguida, há uma renderização de textura e pixel bastante intrincada antes que chegue perto de seu monitor.

Agora que você chegou ao final deste artigo, esperamos que você tenha uma visão mais profunda da jornada de um vértice quando usinado para um quadro 3D. Não cobrimos tudo (é um muazzam artigo!), e temos certeza de que você terá muitas perguntas sobre vetores, matrizes, luzes e elementos primitivos. Solte-os na seção de comentários e faremos o possível para respondê-los.

Leia também