5.6 KiB
LOD multi-resolution avec skirts
Inspire du talk Roblox SIGGRAPH 2020 (p.34-38) et de l'approche Transvoxel. Objectif : augmenter la distance de vue sans exploser le cout de meshing/rendu.
Probleme actuel
- Le monde entier (512x512x256 = 8192 chunks potentiels, ~648 actifs) est meshe et rendu a pleine resolution 32^3
- Le meshing smooth CPU coute 17ms pour 648 chunks (parallelise)
- Le rendu est cheap (0.1ms GPU mesh), mais le meshing smooth bloque le scale-up
- Pas de distance de vue variable : tout ou rien
Approche Roblox : mip pyramid + skirts
Principe
- Chaque chunk stocke un mip pyramid de voxels : 32^3, 16^3, 8^3, 4^3, 2^3, 1^3
- Un octree de rendu decide quel niveau de mip utiliser par chunk (distance camera)
- Les coutures entre chunks de LOD different sont masquees par des skirts
- Les skirts sont des triangles supplementaires avec depth bias dans le VS
Pourquoi des skirts plutot que Transvoxel ?
| Transvoxel | Skirts (Roblox) | |
|---|---|---|
| Complexite | Elevee (tables de cas, 73 transition cells) | Faible (1 couche extra + depth bias) |
| Qualite | Parfaite (mesh continu) | Bonne (gaps invisibles grace au depth bias) |
| Cout meshing | +50% (transition cells) | +15% (1 couche de voxels en plus) |
| Integration | Invasive (change le mesher) | Additive (post-process sur le mesh) |
Plan d'implementation
Phase A : Mip pyramid storage
Fichier : VoxelWorld.h/.cpp
struct Chunk {
VoxelData voxels[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; // LOD 0 (32^3)
// Mip levels stockes a la demande
std::array<std::vector<VoxelData>, 4> mips; // LOD 1-4 (16^3, 8^3, 4^3, 2^3)
uint8_t maxAvailableLOD = 0;
};
Downsampling : pour chaque groupe 2x2x2, le voxel dominant (material le plus frequent, occupancy > 4/8) est conserve. Voxels smooth : l'occupancy est moyennee.
Memoire : un mip pyramid complet = 32^3 + 16^3 + 8^3 + 4^3 + 2^3 = 37448 voxels = ~73 Ko par chunk (vs 64 Ko actuellement). Surcout de 14%.
Phase B : Selection de LOD
Fichier : VoxelRenderer.cpp (dans le frustum cull ou en CPU)
uint8_t selectLOD(const ChunkPos& pos, const XMFLOAT3& cameraPos) {
float dist = distance(chunkCenter(pos), cameraPos);
if (dist < 64.0f) return 0; // pleine resolution
if (dist < 128.0f) return 1; // 16^3
if (dist < 256.0f) return 2; // 8^3
return 3; // 4^3
}
Le LOD est passe au mesher. Le mesher binaire greedy et le Surface Nets travaillent sur le mip correspondant (identiques, juste un tableau plus petit).
Le compute shader voxelMeshCS recoit le LOD level et ajuste le chunk size en
consequence. Les positions des quads sont multipliees par 2^LOD pour rester en
coordonnees monde.
Phase C : Generation des skirts
Principe : quand un chunk a un LOD inferieur (moins detaille) qu'un voisin, des gaps apparaissent a la frontiere. On genere une "jupe" de geometrie supplementaire pour les masquer.
Implementation :
-
Pour chaque face de chunk adjacente a un chunk de LOD superieur :
- Ajouter une couche supplementaire de voxels dupliques depuis le voisin haute-res
- Mesher normalement cette couche (elle s'etend legerement au-dela du chunk)
-
Taguer les vertices de skirt dans le vertex data (1 bit dans les flags)
-
Dans le VS, appliquer un depth bias aux vertices de skirt :
if (isSkirt) { // Pousse le skirt legerement derriere la surface output.position.z += 0.0001; // en clip space (reverse-Z: vers le far) }Le skirt n'est visible que la ou il y a un gap, car la geometrie normale le masque partout ailleurs grace au depth test.
Phase D : Integration rendu
Buffers : les skirts sont inclus dans le meme mega-buffer de quads, tagges par un bit. Pas de draw call supplementaire.
Compute cull : le compute shader de culling (voxelCullCS) recoit le LOD par chunk
dans le GPUChunkInfo. Les chunks LOD > 0 ont moins de quads, donc moins de vertices
a traiter.
RT : les BLAS sont construits par chunk. Les chunks LOD > 0 ont des BLAS plus petits. Le TLAS reste identique.
Phase E : Smooth LOD specifique
Pour les chunks smooth (Surface Nets), le LOD est plus delicat :
- Le mesh smooth LOD 1 (16^3) a des triangles 2x plus grands
- Les normales sont moins precises
- La deformation par materiau (plan-vertex-deformation.md) doit rester coherente
Approche : mesher smooth sur le mip correspondant. Les skirts smooth sont generes de la meme facon (couche supplementaire). La coherence visuelle est acceptable car le smooth est deja "flou" par nature.
Estimation d'effort
| Phase | Effort | Dependance |
|---|---|---|
| A. Mip pyramid | 4h | Aucune |
| B. Selection LOD | 2h | A |
| C. Skirts blocky | 4h | B |
| D. Integration rendu | 3h | C |
| E. Smooth LOD | 4h | B + Phase 5.1 |
| Total | ~17h |
Risques
- Popping : le changement de LOD est visible si les distances sont trop proches. Solution : cross-fade ou hysteresis (changer de LOD a dist+10% pour eviter l'oscillation).
- Skirt artifacts : si le depth bias est trop grand, les skirts sont visibles comme des ombres. Tuner le bias par LOD level.
- Meshing cache : les mips LOD > 0 changent moins souvent. Cacher le mesh par LOD level et ne re-mesher que quand le mip change.
References
- Roblox SIGGRAPH 2020, p.34-38 (skirts + depth bias)
- Transvoxel (Eric Lengyel) : https://transvoxel.org/
- 0fps LOD for blocky voxels : https://0fps.net/2018/03/03/a-level-of-detail-method-for-blocky-voxels/
- Nick Gildea, Dual Contouring seams : http://ngildea.blogspot.com/2014/09/dual-contouring-chunked-terrain.html