¿Cómo renderizar eficientemente una malla de terreno grande?

10

Recientemente me he quedado atrapado en un problema pensando en la mejor manera de generar un terreno en mi juego. En otros proyectos, normalmente usaba mapas de altura, por lo que todo el trabajo central se basaba en el motor utilizado, pero ahora esto no se puede hacer porque el terreno tiene millones de polígonos específicos que deben dibujarse con precisión. Además, muchos de ellos no se pueden analizar desde el vector Y (debido a los polígonos ocultos debajo), es decir, un mapa de altura no es útil aquí. En este caso tuve que usar un objeto COLLADA.

Alguien me dijo que dividiera manualmente el modelo dentro de un software como Blender, pero desafortunadamente esto tampoco es posible porque estos terrenos se crean en fragmentos en otro software y se cargan después en el juego (esa es la idea). Por lo tanto, sería un gran trabajo estar obligado a cortarlos manualmente cada vez.

Por lo tanto, desde una semana he estado estudiando cómo podría resolver este problema y cargar esta malla de procedimiento, el terreno, de acuerdo con la cámara frustrum, ahorrando el mayor rendimiento posible. Encontré muchos documentos sobre la generación de mallas de procedimiento y creo que mi problema podría resolverse mapeando la malla en octrees. Este es un GRAN trabajo, al menos para mí, y es por eso que estoy aquí, porque no quiero arriesgarme a tomar el camino equivocado sin antes escuchar a personas con experiencia.

En resumen, tengo millones de vértices e índices que juntos forman el terreno, pero por razones obvias no puedo dibujarlos al mismo tiempo. Se necesita algún tipo de procedimiento. ¿Cuál es la mejor manera de hacer eso, tratar una gran malla como un terreno? ¿Hay algún libro específico sobre eso? ¿Hay una mejor manera de implementarlo?

Perdón por cualquier tipo de error, soy muy novato en esta área.

Karl Marny
fuente

Respuestas:

12

La fragmentación básica es una buena forma de comenzar. Puede pasar a estructuras de datos más sofisticadas como octrees más adelante, si lo necesita. Por ahora, simplemente divida su terreno en trozos de dimensiones dadas al cargar el modelo desde el disco.

Dependiendo de sus datos, es posible que desee dividir su terreno en pilares en un plano que abarque toda la altura o en cubos en el espacio. El código no está completo (fmod, inicialización de vector, índices, ...) pero debería darte un comienzo.

// Load vertices from disk
struct point { double x, y, z; };    
vector<point> vertices;

// Create container for chunks
typedef pair<int, int> key;
unordered_map<key, vector<point>> chunks;
const int chunksize = 10;

// For each vertex
for (int i = 0; i < vertices.size(); ++i) {
    // Fetch global coordinates
    int x = vertices[i].x,
        y = vertices[i].y,
        z = vertices[i].z;

    // Find containing chunk
    key k;
    k.first  = x / chunksize;
    k.second = z / chunksize;

    // Calculate local coordinates
    point p;
    p.x = x % chunksize;
    p.y = y;
    p.z = z % chunksize;

    // Add to chunk
    chunks[k].push_back(p);
}

// Create separate buffers for each chunk
// ...

Dado que ahora ha dividido la malla, puede realizar LOD y técnicas de eliminación para omitir la representación de fragmentos ocultos.

  • Ver distancia es donde comienzas. Solo representaría fragmentos dentro de una distancia determinada, por ejemplo, la distancia de visualización de su cámara. Cuanto menor sea la distancia de visualización, mayor rendimiento obtendrá, ya que se deben dibujar menos trozos del terreno.

  • La eliminación del frustum es una técnica común para renderizar mallas que se cruzan con el frustum de la vista de la cámara. Esto probablemente le dará la mayor ganancia de rendimiento.

Experimente con el tamaño del fragmento y la distancia de visualización para obtener los mejores resultados. El tamaño del fragmento es una compensación entre el sacrificio preciso y el cálculo fácil. Para optimizar aún más, puede echar un vistazo a estas optimizaciones más avanzadas.

  • La eliminación de la oclusión se puede hacer renderizando las mallas en la CPU a muy baja resolución. Esto le permite detectar temprano mallas ocultas detrás de otras. No tienen que enviarse a la GPU, por lo que guarda muchas ejecuciones de sombreador de vértices que de otro modo se habrían realizado antes de rechazar los triángulos.

  • El nivel de detalle significa que calcula mallas de menor resolución de sus fragmentos. Según la distancia a la cámara, elige una de las mallas para dibujar. Esto le permite reducir la cantidad de vértices, ya que los trozos lejanos no necesitan tantos detalles. Este enfoque funciona bien con los octrees porque podría combinar varios cubos en una malla de baja resolución para áreas alejadas de la cámara. Sin embargo, no es trivial combinar a la perfección los bordes entre dos fragmentos de una resolución diferente.

danijar
fuente