Hoje vou mostrar como abrir uma janela e criar um contexto OpenGL. Essa é uma tarefa surpreendentemente difícil, pois o OpenGL ainda não possui ferramentas oficiais de plataforma cruzada para a criação de contexto; portanto, contaremos com bibliotecas de terceiros (neste caso, GLFW e feliz). Já existem muitos mundos olá semelhantes na Internet, mas não gosto de tudo o que vi: ou é muito sofisticado ou as imagens nos exemplos são muito primitivas (
ou ambas !). Muito obrigado a todos os autores, mas vou baixar outro tutorial :)
Hoje vamos desenhar isso:

Este modelo foi desenhado pelo artista
Samuel Sharit (arshlevon) , muito obrigado por me permitir usá-lo como parte do meu curso de computação gráfica!
Etapa 0: leitura do tinyrenderer
De um modo geral, é melhor (embora não seja necessário) dar essa palestra depois de ler todo o meu curso de
renderizador . Para quem não fala inglês, este curso de palestras está disponível
no hub , embora eu não seja mais compatível com a versão em russo. Como parte deste curso de palestras, mostrei como você pode desenhar apenas essa figura em apenas quinhentas linhas de código e mesmo com a proibição completa de bibliotecas de terceiros:

Surpreendentemente, muitos de meus alunos não entendem que esse rasterizador de software não é apenas um brinquedo, mas uma introdução real de como o OpenGL funciona. Portanto, hoje mostrarei como processar diabetes com aceleração de hardware e, em muitos aspectos, usarei o código do repositório do rasterizador de software.
Atenção, não me proponho a tarefa de explicar todas as linhas de código, pois confio no fato de que ler a documentação é a melhor maneira de entender tudo. Meu código é necessário apenas para saber exatamente o que a documentação deve ler e em que ordem. Além disso, não explicarei o que são shaders e não explicarei como ler mapas normais . Passei muito tempo no tinyrenderer, onde está tudo resolvido.Etapa um, a mais difícil: criar uma janela
O repositório inteiro mora
aqui ; criou um commit para cada etapa do tutorial, já que o github oferece um visualizador muito conveniente de todas as alterações feitas. Começamos
aqui , nosso objetivo é obter esta janela:

O código é compilado usando o CMake; Eu verifiquei no Linux (g ++) e Windows (Visual Studio 2017). No Linux, a versão mais recente do código é compilada assim:
git clone --recurse-submodules https://github.com/ssloy/hellOGL.git cd hellOGL mkdir build cd build cmake .. make
Use `git checkout` se desejar compilar um commit separado, não a versão mais recente. Esse código carrega glad e GLFW, cria uma janela com o retorno de chamada necessário do teclado e carrega shaders de vértice e pixel vazios do disco.
Etapa 2: carregando um modelo 3D
Mudanças no projeto feito nesta fase,
veja aqui . Nesse estágio, nosso objetivo é analisar o arquivo do modelo 3D e desenhar os primeiros triângulos sem se preocupar com a iluminação no momento:

Observe que, tanto o modelo em si quanto a biblioteca para trabalhar com vetores, peguei o analisador de modelo inteiramente no tinyrenderer. Talvez o renderizador de software não seja tão inútil?
A idéia básica no OpenGL moderno é muito simples. Primeiro, carregamos um modelo 3D e, em seguida, crio uma matriz de vértices de tamanho 3 * 3 * (o número de triângulos). Cada triângulo tem três vértices, certo? Cada vértice é descrito por três números (x, y, z). No total, 3 * 3 * model.nfaces () é suficiente para descrevermos o modelo inteiro:
std::vector<GLfloat> vertices(3*3*model.nfaces(), 0); for (int i=0; i<model.nfaces(); i++) { for (int j=0; j<3; j++) { for (int k=0; k<3; k++) vertices[(i*3+j)*3 + k] = model.point(model.vert(i, j))[k]; } }
E então dizemos ao OpenGL que aqui está um array, draw, nativo!
while (!glfwWindowShouldClose(window)) { [...]  glDrawArrays(GL_TRIANGLES, 0, vertices.size()); [...] }
O shader de vértice não faz nada de interessante, simplesmente passa os dados para o shader de fragmento como ele é:
#version 330 core
Bem, o
shader de fragmento também
é despretensioso. Simplesmente desenha o picles atual em vermelho:
#version 330 core
A coisa mais difícil é feita, agora é uma questão de tecnologia!
Estágio três: iluminação difusa
Mudanças no projeto feito nesta fase,
veja aqui . Devemos obter esta imagem:

A iluminação difusa no modelo Phong, como você sabe, é um produto escalar simples entre
vetor normal e vetor de iluminação. Portanto, além da matriz de vértices, adicionei outra matriz de normais. Sem olhar para o código, me diga qual é o tamanho?
O mais interessante acontece no shader de fragmento, no arquivo .cpp principal, apenas os dados são carregados:
#version 330 core
Estágio Quatro: Matrizes de Transformação
Mudanças no projeto feito nesta fase,
veja aqui . Nesse ponto, eu codifiquei as matrizes Modelo, Visualização e Projeção. No início, eles são únicos, mas se você pressionar a barra de espaço, o modelo começará a girar: cada vez que faço um desenho, giro a matriz do modelo em torno do eixo z em 0,01 radianos:
{
Aqui, a função rot_z () retorna a matriz de rotação em torno do eixo z por um determinado ângulo. Como o OpenGL não sabe nada sobre minha classe de matriz, tive que adicionar a exportação de matriz void export_row_major () a um ponteiro simples a um ponto flutuante.

Etapa 5: mapas normais
Mudanças no projeto feito nesta fase,
veja aqui . Neste ponto, aprenderemos como sobrepor texturas. Como a textura difusa usual é chata, aplicarei imediatamente o mapa normal, e mesmo no espaço tangente. Os mapas normais são mais ou menos assim:

Os cálculos correspondentes, para dizer o mínimo, não são óbvios;
leia novamente
as explicações no tinyrenderer . Em termos de dados, você precisa adicionar vários buffers: coordenadas uv e matrizes de vetores tangentes e bi-tangentes.

Estágio Cinco: Textura Difusa
Bem, se já sabemos contar mapas normais, a aplicação de uma textura difusa normal é simplesmente trivial. Mudanças no projeto feito nesta fase,
veja aqui .

Estágio Seis: Brilho
Mudanças no projeto feito nesta fase,
veja aqui . Na etapa final, adicione outra textura que permita simular o brilho da iluminação de superfícies brilhantes:

Conclusão
Há muito que pode ser aprimorado nesse código e os efeitos visuais podem ser distorcidos infinitamente. Mas esse não é meu objetivo, meu objetivo é mostrar que absolutamente todas as técnicas que abordamos na renderização de software são aplicáveis no contexto atual do OpenGL. E, pessoalmente, ainda acho que você precisa familiarizar-se com gráficos 3D desenhando sem usar a magia das bibliotecas gráficas.
Como uma extensão, tente, por exemplo,
adicionar sombras ou contar
a iluminação global ou, finalmente, fazer um mapa de brilho: afinal, os olhos e o cristal na testa de Diablo devem brilhar!