Estou criando um jogo XNA que requer um espaço enorme para os jogadores. Atualmente, o mapa de altura de teste que estou usando é 4096x4096 e é salvo como um BMP de 4 bits.
O que estou tentando fazer é pegar esse enorme arquivo de altura e renderizá-lo no jogo. O problema que estou enfrentando é o fato de que é ineficiente carregar todo o terreno na memória de uma só vez, pois ele usará a maior parte da memória disponível.
Outro problema com o qual me deparei é que não consigo processar o terreno em um único primitivo por causa de um limite definido no XNA.
Com isso dito, deparei-me com várias soluções, todas listadas abaixo:
- Renderização com base na localização do usuário atual - basicamente desenhando um quadrado ao redor do usuário, independentemente da sua orientação no mundo. Isso também não é exatamente o que eu queria, porque você ainda está processando um espaço que o usuário não vê.
- Renderização com base na orientação e posição do usuário - Encontrei uma fórmula para recuperar um triângulo que deveria ter quais pixels do mapa de altura deveriam ser renderizados, mas isso se mostrou muito difícil.
- Dividir o terreno em vários pedaços e renderizar quais são os mais próximos do usuário - Ainda não é muito eficiente, pois você ainda está processando pedaços que as pessoas não verão. E é trabalhoso, pois preciso dividir meu mapa de altura em várias partes, e a escalabilidade se torna um grande problema.
Depois de tentar essas soluções, estou sem ideias para o que fazer. Recebi algumas respostas em que as pessoas estão me dizendo para fazer esses algoritmos complexos, mas simplesmente não tenho idéia de como abordá-los.
Então, basicamente, estou pedindo uma maneira simples e direta de renderizar terrenos enormes no XNA com a máxima eficiência.
Sou um pouco novo no desenvolvimento de jogos em geral, mas estou disposto a pesquisar se parecer promissor.
Atualização 1: Após pesquisar o método de geoclipmapping, comecei a codificar com isso. Eu tenho toda a matemática feita, e o jogo corre. No entanto, é extremamente ineficiente - o que provavelmente é uma má codificação da minha parte. Ele roda a 2FPS e usa um núcleo inteiro da minha CPU. Vou tentar melhorar o código, mas acho que vou precisar de mais ajuda, então aqui está um Pastebin do código da classe de gerenciador de terreno. Mais tarde, postarei mais resultados se conseguir que seja mais eficiente.
Respostas:
A abordagem de chunks é normalmente o que é usado. Raramente é eficiente testar todos os triângulos de centenas de milhares para ver se você deve renderizá-lo. Em vez disso, a maioria dos algoritmos de renderização de terreno emprega uma estrutura de dados espaciais para renderizar dinamicamente as partes visíveis do terreno.
Uma estrutura de dados fácil de implementar é chamada de quadtree . Em resumo, para usar um quadtree, você encontrará o frustum de visualização do jogador, o intercepta contra o nível superior do quadtree e, para todos os pedaços parcialmente visíveis (ou seja, os planos de frustum cruzam o pedaço), você subdivide e testa todas as crianças pedaços, omitindo fora do frustum. Isso fornecerá uma aproximação bem próxima da geometria visível real, com apenas alguns níveis de recursão.
Os renderizadores de terreno mais avançados usam um algoritmo para ajustar não apenas a geometria visível, mas também os detalhes dessa geometria. O geomipmapping (e seu relativo geoclipmapping) é relativamente popular no momento por fazer isso, mas não é algo trivial para implementar.
edit: Aqui está uma descrição decente do geoclipmapping e do frustum culling.
Também tenho algumas dúvidas sobre se 4 bits para o mapa de altura são realmente suficientes para produzir um terreno bonito, a menos que você esteja suavizando bastante o resultado.
fonte
Qualquer abordagem que exija que você faça um trabalho por quadro para carregar dados na GPU será falha.
Aqui está um esboço de uma abordagem que deve ter um bom desempenho:
Você deve dividir seu terreno em blocos (razoavelmente grandes), carregando esses blocos em buffers de vértice fixos (a profundidade de bits do seu mapa de altura não importa!). Esses buffers de vértice simplesmente ficam na memória da GPU, aguardando a renderização. Você terá que experimentar um tamanho de bloco apropriado, mas 128 x 128 é talvez um bom lugar para começar.
Para um terreno de 4096 x 4096, você está um pouco além do limite do que seria confortável carregar na GPU de uma só vez - provavelmente são algumas centenas de MB de dados de vértice (embora você possa reduzi-lo para ~ 64 MB, se estiver esperto). Portanto, pode ser necessário carregar e descarregar buffers de vértice da GPU em segundo plano.
(Se você implementar o carregamento em segundo plano de pedaços, isso deve ser extremamente escalável!)
Depois de ter os dados do vértice na GPU, é um momento apropriado para selecionar a visibilidade por bloco . Não é necessário enviar o comando para renderizar um pedaço, se você souber que está atrás da câmera.
Você quase nunca deve fazer a seleção por triângulo na CPU!
A GPU seleciona triângulos fora da tela muito mais rapidamente do que você jamais conseguirá.
Para obter mais informações sobre desempenho, consulte esta resposta no site Game Dev.
fonte
Não sou especialista em XNA, por qualquer meio, por favor, corrija-me se estiver errado, mas fiquei com a impressão de que há realmente otimização embutida para situações como essa. Eu sei que você é capaz de definir uma distância de renderização e, depois desse ponto, ela não renderiza nada; no seu caso, seria o terreno restante. No entanto, isso deixa uma vantagem pouco atraente para o seu mundo renderizado, então você teria que implementar algo como o embaçamento que a maioria dos jogos de mundo aberto tem.
fonte