Eu já implementei cubos de marchas / tetraedros para renderizar um IsoSurface. Funcionou ( YouTube ), mas o desempenho foi péssimo, pois nunca consegui implementar o Nível de detalhe variável com base na distância da visualização (ou mesmo remover pedaços antigos e distantes).
Decidi tentar outra vez e fazê-lo corretamente desta vez. Comecei criando um OctreeNode que funciona da seguinte maneira quando Build()
é chamado.
- Se o pedaço for muito pequeno para ser construído, retorne imediatamente.
- Calcule se a superfície passa pelo volume desse pedaço.
- Nesse caso, decida se queremos aumentar o LOD (porque a câmera está próxima)
- Nesse caso, crie 8 filhos e chame o mesmo processo para eles
- Caso contrário, construa a malha usando as dimensões do nó atual
Algum PseudoCode:
OctNode Build() {
if(this.ChunkSize < minChunkSize) {
return null;
}
densityRange = densitySource¹.GetDensityRange(this.bounds);
if(densityRange.min < surface < densityRange.max) {
if(loDProvider.DesiredLod(bounds)² > currentLoD) {
for(i 1 to 8) {
if(children[i] == null) {
children[i] = new OctNode(...)
}
children[i] = children[i].Build();
}
} else {
BuildMesh();
}
return this;
}
}
¹ Além de retornar a densidade em um ponto, a fonte de densidade pode determinar a faixa de densidade possível para um determinado volume.
² O provedor LoD pega uma caixa delimitadora e retorna o LoD máximo desejado com base na posição da câmera / perfil, configurações do usuário, etc.
Então ... Tudo isso funciona muito bem. Usando uma esfera simples como fonte de densidade e mostrando todos os nós:
E apenas as folhas:
No entanto, existem alguns problemas:
- Eu tenho que definir o volume inicial delimitador (e quanto maior, mais processamento eu preciso fazer)
- Na raiz da árvore, não tenho idéia de quão profundas serão as folhas, então minha numeração LoD começa na menor qualidade (raiz) e aumenta à medida que os pedaços ficam menores. Como o LoD agora é relativo ao volume inicial, não é muito útil quando quero fazer coisas em tamanhos / qualidades específicos.
Pensei em algumas opções, mas ambas parecem falhas:
- Mantenha uma coleção de Octrees e adicione / remova dependendo da distância. Não consigo ver como eu faria a malha bem¹, além de precisar de uma lista de nós vazios conhecidos, especialmente se eu quiser superfícies 3D arbitrárias (para evitar recalcular volumes vazios repetidamente)
- Adicione um nó pai à raiz atual e adicione sete irmãos para o nó original. Isso funcionaria e seria sob demanda, mas parece complexo recuar sensivelmente à medida que o jogador se move pela paisagem. Isso também tornaria os números LoD ainda menos significativos.
In [Para esclarecer Q abaixo] No momento, se 2 nós fisicamente adjacentes na árvore estiverem em LODs diferentes, eu tenho algum código para coagir os verts de modo que não haja costura quando as malhas são geradas. Sou capaz de fazer isso conhecendo a densidade de vários nós circundantes. Em um cenário em que eu tenho 2 octrees independentes lado a lado, eu não teria uma maneira fácil de recuperar essas informações, resultando em costuras.
Qual é a melhor maneira de abordar isso?