Decidi escrever um pouco sobre o aspecto da programação e como os componentes se comunicam. Talvez isso possa esclarecer algumas áreas.
A apresentação
O que é preciso para ter essa imagem única, que você postou em sua pergunta, desenhada na tela?
Existem várias maneiras de desenhar um triângulo na tela. Para simplificar, vamos assumir que nenhum buffer de vértice foi usado. (Um buffer de vértice é uma área da memória onde você armazena coordenadas.) Vamos assumir que o programa simplesmente disse ao pipeline de processamento de gráficos sobre cada vértice (um vértice é apenas uma coordenada no espaço) em uma linha.
Mas , antes que possamos desenhar qualquer coisa, primeiro precisamos executar alguns andaimes. Veremos o porquê mais tarde:
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Drawing Using Triangles
glBegin(GL_TRIANGLES);
// Red
glColor3f(1.0f,0.0f,0.0f);
// Top Of Triangle (Front)
glVertex3f( 0.0f, 1.0f, 0.0f);
// Green
glColor3f(0.0f,1.0f,0.0f);
// Left Of Triangle (Front)
glVertex3f(-1.0f,-1.0f, 1.0f);
// Blue
glColor3f(0.0f,0.0f,1.0f);
// Right Of Triangle (Front)
glVertex3f( 1.0f,-1.0f, 1.0f);
// Done Drawing
glEnd();
Então o que isso fez?
Quando você escreve um programa que deseja usar a placa gráfica, geralmente escolhe algum tipo de interface para o driver. Algumas interfaces bem conhecidas para o driver são:
Neste exemplo, ficaremos com o OpenGL. Agora, sua interface com o driver é o que fornece todas as ferramentas necessárias para que o seu programa fale com a placa gráfica (ou com o driver, que depois fala com a placa).
Essa interface é obrigada a fornecer algumas ferramentas . Essas ferramentas assumem a forma de uma API que você pode chamar do seu programa.
Essa API é o que vemos sendo usado no exemplo acima. Vamos olhar mais de perto.
Andaimes
Antes de realmente fazer qualquer desenho real, você terá que realizar uma configuração . Você precisa definir sua janela de exibição (a área que será renderizada), sua perspectiva (a câmera em seu mundo), que anti-aliasing você estará usando (para suavizar as bordas do triângulo) ...
Mas não vamos olhar para nada disso. Vamos dar uma olhada nas coisas que você terá que fazer em todos os quadros . Gostar:
Limpando a tela
O pipeline de gráficos não limpará a tela de todos os quadros. Você terá que contar. Por quê? Isso é por que:
Se você não limpar a tela, basta desenhar sobre ela todos os quadros. É por isso que ligamos glClear
com o GL_COLOR_BUFFER_BIT
aparelho. O outro bit ( GL_DEPTH_BUFFER_BIT
) diz ao OpenGL para limpar o buffer de profundidade . Esse buffer é usado para determinar quais pixels estão na frente (ou atrás) de outros pixels.
Transformação
Fonte da imagem
Transformação é a parte em que pegamos todas as coordenadas de entrada (os vértices de nosso triângulo) e aplicamos nossa matriz ModelView. Essa é a matriz que explica como nosso modelo (os vértices) é girado, dimensionado e convertido (movido).
Em seguida, aplicamos nossa matriz de projeção. Isso move todas as coordenadas para que elas fiquem de frente para nossa câmera.
Agora, nos transformamos mais uma vez, com nossa matriz Viewport. Fazemos isso para dimensionar nosso modelo para o tamanho de nosso monitor. Agora temos um conjunto de vértices prontos para serem renderizados!
Voltaremos à transformação um pouco mais tarde.
Desenhando
Para desenhar um triângulo, podemos simplesmente dizer ao OpenGL para iniciar uma nova lista de triângulos chamando glBegin
com a GL_TRIANGLES
constante.
Também há outras formas que você pode desenhar. Como uma faixa triangular ou um ventilador triangular . Essas são principalmente otimizações, pois exigem menos comunicação entre a CPU e a GPU para desenhar a mesma quantidade de triângulos.
Depois disso, podemos fornecer uma lista de conjuntos de 3 vértices que devem compor cada triângulo. Cada triângulo usa três coordenadas (como estamos no espaço 3D). Além disso, também forneço uma cor para cada vértice, chamando glColor3f
antes de chamar glVertex3f
.
A sombra entre os três vértices (os três cantos do triângulo) é calculada automaticamente pelo OpenGL . Ele interpola a cor por toda a face do polígono.
Interação
Agora, quando você clica na janela. O aplicativo precisa capturar apenas a mensagem da janela que sinaliza o clique. Em seguida, você pode executar qualquer ação em seu programa que desejar.
Isso fica muito mais difícil quando você deseja começar a interagir com sua cena 3D.
Você primeiro precisa saber claramente em qual pixel o usuário clicou na janela. Depois, levando em consideração sua perspectiva , você pode calcular a direção de um raio, a partir do ponto em que o mouse clica na cena. Você pode calcular se algum objeto em sua cena se cruza com esse raio . Agora você sabe se o usuário clicou em um objeto.
Então, como você faz girar?
Transformação
Estou ciente de dois tipos de transformações geralmente aplicadas:
- Transformação baseada em matriz
- Transformação óssea
A diferença é que os ossos afetam vértices únicos . Matrizes sempre afetam todos os vértices desenhados da mesma maneira. Vejamos um exemplo.
Exemplo
Anteriormente, carregamos nossa matriz de identidade antes de desenhar nosso triângulo. A matriz de identidade é aquela que simplesmente não fornece nenhuma transformação . Então, o que quer que eu desenhe, é afetado apenas pela minha perspectiva. Portanto, o triângulo não será rotacionado.
Se eu quiser girar agora, eu poderia fazer as contas sozinho (na CPU) e simplesmente chamar glVertex3f
com outras coordenadas (que são giradas). Ou eu poderia deixar a GPU fazer todo o trabalho, ligando glRotatef
antes de desenhar:
// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);
amount
é claro que é apenas um valor fixo. Se você deseja animar , precisará acompanhar amount
e aumentá-lo a cada quadro.
Então, espere, o que aconteceu com toda a conversa da matriz antes?
Neste exemplo simples, não precisamos nos preocupar com matrizes. Simplesmente chamamos glRotatef
e isso cuida de tudo isso para nós.
glRotate
produz uma rotação de angle
graus ao redor do vetor xyz. A matriz atual (consulte glMatrixMode ) é multiplicada por uma matriz de rotação com o produto substituindo a matriz atual, como se glMultMatrix fosse chamado com a seguinte matriz como argumento:
x 2 1 - c + cx y 1 - c - z sx z - 1 - c + y s 0 y x 1 - c + z sy 2 1 - c + cy z 1 - c - x 0 s 0 x z y 1 - c - y sy z 1 - c + x sz 2 1 - c + c 0 0 0 0 1
Bem, obrigado por isso!
Conclusão
O que se torna óbvio é que há muita conversa com o OpenGL. Mas não está nos dizendo nada. Onde está a comunicação?
A única coisa que o OpenGL está nos dizendo neste exemplo é quando terminar . Toda operação levará um certo tempo. Algumas operações demoram incrivelmente tempo, outras são incrivelmente rápidas.
Enviar um vértice para a GPU será tão rápido que eu nem saberia como expressá-lo. Enviar milhares de vértices da CPU para a GPU, todos os quadros, provavelmente não é problema.
A limpeza da tela pode levar um milissegundo ou pior (lembre-se de que você normalmente tem apenas 16 milissegundos de tempo para desenhar cada quadro), dependendo do tamanho da sua viewport. Para limpá-lo, o OpenGL precisa desenhar todos os pixels da cor que você deseja limpar, que podem ser milhões de pixels.
Fora isso, podemos apenas perguntar ao OpenGL sobre os recursos do nosso adaptador gráfico (resolução máxima, anti-aliasing máximo, profundidade máxima de cor, ...).
Mas também podemos preencher uma textura com pixels, cada um com uma cor específica. Cada pixel possui um valor e a textura é um "arquivo" gigante preenchido com dados. Podemos carregar isso na placa de vídeo (criando um buffer de textura), depois carregar um shader , dizer a ele para usar nossa textura como entrada e executar alguns cálculos extremamente pesados em nosso "arquivo".
Podemos então "renderizar" o resultado de nossa computação (na forma de novas cores) em uma nova textura.
É assim que você pode fazer a GPU funcionar para você de outras maneiras. Presumo que o CUDA tenha um desempenho semelhante a esse aspecto, mas nunca tive a oportunidade de trabalhar com ele.
Nós realmente apenas tocamos um pouco o assunto todo. A programação de gráficos 3D é um inferno.
Fonte da imagem