Uso o OpenGL há algum tempo e li um grande número de tutoriais. Além do fato de muitos deles ainda usarem o pipeline fixo, eles geralmente lançam toda a inicialização, alterações de estado e desenho em um arquivo de origem. Isso é bom para o escopo limitado de um tutorial, mas estou tendo dificuldades para descobrir como dimensioná-lo para um jogo completo.
Como você divide o uso do OpenGL entre arquivos? Conceitualmente, posso ver os benefícios de ter, digamos, uma classe de renderização que processa puramente coisas para a tela, mas como funcionariam coisas como sombreadores e luzes? Devo ter aulas separadas para coisas como luzes e shaders?
c++
opengl
architecture
objects
Sullivan
fonte
fonte
Respostas:
Eu acho que o OO OpenGL não é tão necessário. É diferente quando você fala sobre shader, modelo, classe etc.
Basicamente, você faria a inicialização do jogo / mecanismo primeiro (e outras coisas). Em seguida, você carregaria texturas, modelos e shaders na RAM (se necessário) e nos Buffer Objects, além de carregar / compilar shaders. Depois disso, você, em sua estrutura de dados ou classe de shader, modelo, possui IDs int de shaders, objetos de buffer de modelo e textura.
Eu acho que a maioria dos mecanismos tem componentes de mecanismo e todos eles têm determinadas interfaces. Todos os mecanismos que eu analisei têm algum componente, como Renderer, SceneManager ou ambos (depende da complexidade do jogo / mecanismo). Do que você pode ter a classe OpenGLRenderer e / ou DXRenderer que implementam a interface Renderer. Então, se você tiver o SceneManager e o Renderer, poderá executar algumas das seguintes ações:
O renderizador provavelmente chamaria a função de desenho do objeto, que chamaria a função de desenho de cada malha composta e a malha vincularia o objeto de textura, vincularia o sombreador, chamaria a função de desenho do OpenGL e depois usaria sombreadores, objetos de buffer de dados de textura e objeto.
NOTA: este é apenas um exemplo, você deve estudar o SceneManager com mais detalhes e analisar seu caso de uso para ver qual é a melhor opção de implementação
Obviamente, você teria outros componentes do mecanismo, como MemoryManager , ResourceLoader etc., que cuidariam do uso de memória de vídeo e RAM, para que eles pudessem carregar / descarregar certos modelos / shaders / texturas, conforme necessário. Os conceitos para isso incluem cache de memória, mapeamento de memória, etc. etc. Existem muitos detalhes e conceitos sobre cada componente.
Dê uma olhada na descrição mais detalhada de outros mecanismos de jogos, existem muitos deles e a documentação deles está disponível.
Mas sim, as aulas facilitam a vida; você deve usá-los totalmente e lembrar-se de encapsulamento, herança, interfaces e outras coisas interessantes.
fonte
O OpenGL já possui alguns conceitos de 'Objeto'.
Por exemplo, qualquer coisa com um ID pode passar como um objeto (também existem itens especificamente chamados 'Objetos'). Buffers, Texturas, Objetos de buffer de vértice, Objetos de matriz de vértice, Objetos de buffer de quadro e assim por diante. Com um pouco de trabalho, você pode agrupar as aulas em torno deles. Ele também fornece uma maneira fácil de você voltar a funções obsoletas do OpenGL, se o seu contexto não suportar as extensões. Por exemplo, um VertexBufferObject pode voltar a usar glBegin (), glVertex3f () e assim por diante.
Existem algumas maneiras de você precisar se afastar dos conceitos tradicionais do OpenGL; por exemplo, você provavelmente deseja armazenar metadados sobre os buffers nos objetos de buffer. Por exemplo, se o buffer armazena vértices. Qual é o formato dos vértices (ou seja, posição, normais, cabos de texto e assim por diante). Quais primitivas ele usa (GL_TRIANGLES, GL_TRIANGLESTRIP, etc ...), informações de tamanho (quantos carros alegóricos são armazenados, quantos triângulos eles representam, etc ...). Apenas para facilitar a conexão deles nos comandos draw arrays.
Eu recomendo que você olhe para o OGLplus . São ligações C ++ para OpenGL.
Também glxx , isso é apenas para carregamento de extensões.
Além de agrupar a API do OpenGL, você deve criar um nível um pouco mais alto sobre ele.
Por exemplo, uma classe de gerente de materiais responsável por todos os shaders, carregando e usando-os. Também seria responsável por transferir propriedades para eles. Dessa forma, você pode simplesmente chamar: materials.usePhong (); material.setTexture (alguma textura); material.setColor (). Isso permite mais flexibilidade, já que você pode usar coisas mais recentes, como objetos de buffer uniforme compartilhado, para ter apenas um grande buffer contendo todas as propriedades que seus shaders usam em um bloco, mas se isso não for suportado, você volta a carregar em cada programa de shader. Você pode ter um grande shader monolítico e alternar entre diferentes modelos de shader usando rotinas uniformes, se houver suporte, ou pode voltar a usar vários shaders pequenos e diferentes.
Você também pode analisar as especificações GLSL para escrever seu código de sombreador. Por exemplo, o #include seria incrivelmente útil e muito fácil de implementar no código de carregamento do shader (também existe uma extensão ARB ). Você também pode gerar seu código rapidamente, com base em quais extensões são suportadas, por exemplo, usar um objeto uniforme compartilhado ou voltar a usar uniformes normais.
Finalmente, você desejará uma API de pipeline de renderização de nível superior que faça coisas como gráficos de cena, efeitos especiais (desfoque, brilho), coisas que exijam várias passagens de renderização, como sombras, iluminação e outras coisas. Além disso, uma API de jogo que não tem nada a ver com a API gráfica, mas apenas lida com objetos em um mundo.
fonte
oglplus::Context
classe torna essa dependência altamente visível - isso seria um problema? Acredito que ajudará os novos usuários do OpenGL a evitar muitos problemas.No OpenGL moderno, você pode separar quase totalmente objetos renderizados, usando diferentes vaos e programas de sombreamento. E mesmo a implementação de um objeto pode ser separada em várias camadas de abstração.
Por exemplo, se você deseja implementar um terreno, você pode definir um TerrainMesh cujo construtor cria os vértices e os índices para o terreno e os define em buffers de matriz e - se você atribuir uma posição de atributo - ele faz sombrear seus dados. Ele também deve saber como renderizá-lo e deve-se reverter todas as alterações de contexto feitas para configurar a renderização. Essa classe em si não deve saber nada sobre o programa de sombreador que a renderizará e também não deve saber nada sobre outros objetos na cena. Acima dessa classe, você pode definir um Terreno, que conhece o código do sombreador, e seu trabalho é criar a conexão entre o sombreador e o TerrainMesh. Isso deve significar obter atributos e posições uniformes, carregar texturas e coisas assim. Esta classe não deve saber nada sobre como o terreno é implementado, qual algoritmo LoD usa, é apenas responsável por sombrear o terreno. Acima disso, você pode definir a funcionalidade que não é do OpenGL, como behivour e detecção de colisão e outras.
Chegando ao ponto, mesmo que o OpenGL seja projetado para ser usado em nível baixo, você ainda pode criar camadas independentes de abstração, que permitem escalar para aplicações com o tamanho de um jogo Unreal. Mas o número de camadas que você deseja / precisa realmente depende do tamanho do aplicativo que deseja.
Mas não minta para si mesmo sobre esse tamanho, não tente imitar o modelo de objeto do Unity em um aplicativo de linha de 10k; o resultado será um desastre completo. Crie as camadas gradualmente, aumente apenas o número de camadas de abstração, quando necessário.
fonte
O ioDoom3 é provavelmente um ótimo ponto de partida, pois você pode confiar no Carmack para seguir boas práticas de codificação. Também acredito que ele não usa megatexturing no Doom3, por isso é relativamente direto como um tubo de renderização.
fonte