Anteriormente he implementado cubos de marcha / tetraedros para representar un IsoSurface. Funcionó ( YouTube ), pero el rendimiento fue terrible ya que nunca pude implementar un Nivel de detalle variable basado en la distancia de visualización (o incluso eliminar fragmentos viejos y distantes).
Decidí probar otra vez y hacerlo bien esta vez. Comencé creando un OctreeNode que funciona de la siguiente manera cuando Build()
se llama.
- Si el fragmento es demasiado pequeño para construirlo, regrese de inmediato.
- Averigüe si la superficie pasa a través del volumen de este fragmento.
- Si es así, decida si queremos elevar el LOD (porque la cámara está cerca)
- Si es así, genera 8 niños y llama al mismo proceso
- Si no, construya la malla usando las dimensiones del nodo actual
Algunos pseudocódigo:
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;
}
}
¹ Además de devolver la densidad en un punto, la fuente de densidad puede determinar el rango de densidad posible para un volumen dado.
² El proveedor de LoD toma un cuadro delimitador y devuelve el LoD máximo deseado en función de la posición / frustum de la cámara, la configuración del usuario, etc.
Entonces ... Todo esto funciona bastante bien. Usando una esfera simple como la fuente de densidad, y mostrando todos los nodos:
Y solo las hojas:
Sin embargo, hay un par de problemas:
- Tengo que definir el volumen delimitador inicial (y cuanto más grande sea, más procesamiento necesito hacer)
- En la raíz del árbol, no tengo idea de qué tan profundas serán las hojas, por lo que mi numeración LoD comienza con la calidad más baja (raíz) y aumenta a medida que los trozos se hacen más pequeños. Debido a que LoD ahora es relativo al volumen inicial, no sirve de mucho cuando quiero hacer cosas en tamaños / calidades específicos.
He pensado en un par de opciones, pero ambas parecen defectuosas:
- Mantenga una colección de octrees y agregue / elimine según la distancia. No puedo ver cómo encajaría bien, además, necesitaría una lista de nodos vacíos conocidos, especialmente si quiero superficies 3D arbitrarias (para evitar volver a calcular volúmenes vacíos repetidamente)
- Agregue un nodo primario a la raíz actual, luego agregue siete hermanos para el nodo original. Esto funcionaría y sería a pedido, pero parece complejo reducirlo sensiblemente a medida que el jugador se mueve por el paisaje. También haría que los números de LoD fueran aún menos significativos.
¹ [En aclaración a Q a continuación] En la actualidad, si 2 nodos físicamente adyacentes en el árbol están en LOD diferentes, tengo algún código para forzar a los verts de modo que no haya costura cuando se generan las mallas. Puedo hacer esto conociendo la densidad para múltiples nodos circundantes. En un escenario en el que tengo 2 octreos independientes uno al lado del otro, no tendría una manera fácil de recuperar esta información, lo que daría lugar a costuras.
¿Cuál es una forma óptima de abordar esto?