147 lines
5.6 KiB
Markdown
147 lines
5.6 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
1. Chaque chunk stocke un **mip pyramid** de voxels : 32^3, 16^3, 8^3, 4^3, 2^3, 1^3
|
||
|
|
2. Un **octree** de rendu decide quel niveau de mip utiliser par chunk (distance camera)
|
||
|
|
3. Les coutures entre chunks de LOD different sont masquees par des **skirts**
|
||
|
|
4. 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`
|
||
|
|
|
||
|
|
```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)
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
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 :**
|
||
|
|
|
||
|
|
1. 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)
|
||
|
|
|
||
|
|
2. Taguer les vertices de skirt dans le vertex data (1 bit dans les flags)
|
||
|
|
|
||
|
|
3. Dans le VS, appliquer un **depth bias** aux vertices de skirt :
|
||
|
|
```hlsl
|
||
|
|
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
|