141 lines
5.4 KiB
Markdown
141 lines
5.4 KiB
Markdown
|
|
# Deformation de vertices par materiau
|
||
|
|
|
||
|
|
Inspiree du talk Roblox SIGGRAPH 2020 (p.19). Chaque materiau definit une deformation
|
||
|
|
procedurale appliquee aux vertices Surface Nets apres le calcul du centroide.
|
||
|
|
Donne un caractere visuel distinct a chaque materiau sans cout GPU supplementaire.
|
||
|
|
|
||
|
|
## Objectif
|
||
|
|
|
||
|
|
Actuellement, tous les materiaux smooth produisent les memes blobs lisses uniformes.
|
||
|
|
Avec la deformation par materiau :
|
||
|
|
- La **pierre** aurait des aretes plus marquees (cubify)
|
||
|
|
- Le **sable** aurait des surfaces ondulees (shift)
|
||
|
|
- La **neige** resterait lisse (aucune deformation)
|
||
|
|
- La **glace** aurait des facettes cristallines (quantize)
|
||
|
|
|
||
|
|
## Modes de deformation (Roblox)
|
||
|
|
|
||
|
|
| Mode | Effet | Formule | Materiaux cibles |
|
||
|
|
|------|-------|---------|-----------------|
|
||
|
|
| `None` | Aucune deformation | identity | snow, water |
|
||
|
|
| `Shift` | Offset pseudo-random | `pos += hash(pos) * amplitude` | sand, dirt |
|
||
|
|
| `Cubify` | Lerp vers centre du cube | `pos = lerp(pos, round(pos) + 0.5, factor)` | stone, rock |
|
||
|
|
| `Quantize` | Arrondi a pas fixe | `pos = round(pos * K) / K` | ice, crystal |
|
||
|
|
| `Barrel` | Cubify uniquement en Y | `pos.y = lerp(pos.y, round(pos.y) + 0.5, f)` | pillars, trunks |
|
||
|
|
|
||
|
|
## Integration dans le code existant
|
||
|
|
|
||
|
|
### 1. Etendre MaterialDesc (VoxelTypes.h)
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
struct MaterialDesc {
|
||
|
|
// ... champs existants ...
|
||
|
|
uint8_t deformMode = 0; // 0=None, 1=Shift, 2=Cubify, 3=Quantize, 4=Barrel
|
||
|
|
uint8_t deformStrength = 0; // 0-255 -> 0.0-1.0
|
||
|
|
// remplace _pad ou ajoute 2 bytes (struct reste 16-aligned)
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
Pas de changement GPU : la deformation est CPU-only dans le mesher.
|
||
|
|
|
||
|
|
### 2. Modifier SmoothMesher (VoxelMesher.cpp)
|
||
|
|
|
||
|
|
Le point d'insertion est apres le calcul du centroide, avant l'ecriture dans le buffer
|
||
|
|
de sortie. Actuellement dans `meshSurfaceNets()` :
|
||
|
|
|
||
|
|
```
|
||
|
|
centroid = average(edge_crossings)
|
||
|
|
normal = average(triangle_normals)
|
||
|
|
-> INSERER DEFORMATION ICI
|
||
|
|
write SmoothVertex(centroid, normal, material)
|
||
|
|
```
|
||
|
|
|
||
|
|
Implementation :
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
// Apres le calcul du centroid et avant l'ecriture du vertex
|
||
|
|
XMFLOAT3 deformVertex(XMFLOAT3 pos, const MaterialDesc& mat) {
|
||
|
|
switch (mat.deformMode) {
|
||
|
|
case 1: { // Shift
|
||
|
|
float strength = mat.deformStrength / 255.0f;
|
||
|
|
// Hash stable base sur position entiere (pas de flicker en animation)
|
||
|
|
uint32_t h = hash3(int(pos.x), int(pos.y), int(pos.z));
|
||
|
|
float rx = ((h & 0xFF) / 255.0f - 0.5f) * strength;
|
||
|
|
float ry = (((h >> 8) & 0xFF) / 255.0f - 0.5f) * strength;
|
||
|
|
float rz = (((h >> 16) & 0xFF) / 255.0f - 0.5f) * strength;
|
||
|
|
return { pos.x + rx, pos.y + ry, pos.z + rz };
|
||
|
|
}
|
||
|
|
case 2: { // Cubify
|
||
|
|
float f = mat.deformStrength / 255.0f;
|
||
|
|
float cx = floorf(pos.x) + 0.5f;
|
||
|
|
float cy = floorf(pos.y) + 0.5f;
|
||
|
|
float cz = floorf(pos.z) + 0.5f;
|
||
|
|
return { lerp(pos.x, cx, f), lerp(pos.y, cy, f), lerp(pos.z, cz, f) };
|
||
|
|
}
|
||
|
|
case 3: { // Quantize
|
||
|
|
float K = 2.0f + (mat.deformStrength / 255.0f) * 6.0f; // 2-8 steps
|
||
|
|
return { roundf(pos.x * K) / K, roundf(pos.y * K) / K, roundf(pos.z * K) / K };
|
||
|
|
}
|
||
|
|
case 4: { // Barrel (cubify Y only)
|
||
|
|
float f = mat.deformStrength / 255.0f;
|
||
|
|
float cy = floorf(pos.y) + 0.5f;
|
||
|
|
return { pos.x, lerp(pos.y, cy, f), pos.z };
|
||
|
|
}
|
||
|
|
default: return pos;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Recalculer les normales apres deformation
|
||
|
|
|
||
|
|
Les normales moyennees doivent etre recalculees APRES la deformation, sinon elles ne
|
||
|
|
correspondent plus a la geometrie deformee. Deux options :
|
||
|
|
|
||
|
|
**Option A (simple) :** Recalculer les face normals des triangles adjacents apres deformation.
|
||
|
|
C'est ce que fait deja le pass de normales dans `meshSurfaceNets()`, il suffit de le
|
||
|
|
deplacer apres la deformation.
|
||
|
|
|
||
|
|
**Option B (rapide) :** Garder les normales originales. La deformation est subtile,
|
||
|
|
l'erreur de normale est visuellement acceptable. Recommande pour le prototype.
|
||
|
|
|
||
|
|
### 4. Soft/hard edges par materiau
|
||
|
|
|
||
|
|
Roblox controle aussi les aretes douces/dures par materiau. On peut ajouter :
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
uint8_t edgeHardness = 0; // 0=smooth normals, 255=flat/geometric normals
|
||
|
|
```
|
||
|
|
|
||
|
|
Dans le PS, interpoler entre les smooth normals et les geometric normals (deja
|
||
|
|
disponibles via le triplanar). Cout zero cote mesher, petit calcul PS.
|
||
|
|
|
||
|
|
### 5. Configurer les materiaux existants
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
// Dans VoxelWorld::initMaterials() ou equivalent
|
||
|
|
materials[5].deformMode = 0; materials[5].deformStrength = 0; // snow: lisse
|
||
|
|
materials[3].deformMode = 2; materials[3].deformStrength = 180; // stone: cubify fort
|
||
|
|
materials[6].deformMode = 2; materials[6].deformStrength = 100; // smoothstone: cubify leger
|
||
|
|
materials[4].deformMode = 1; materials[4].deformStrength = 60; // sand: shift subtil
|
||
|
|
materials[2].deformMode = 1; materials[2].deformStrength = 30; // dirt: shift tres leger
|
||
|
|
```
|
||
|
|
|
||
|
|
## Risques et precautions
|
||
|
|
|
||
|
|
- **Self-intersection** : une deformation trop forte peut creer des triangles inverses.
|
||
|
|
Limiter `deformStrength` a ~200 max et verifier visuellement.
|
||
|
|
- **Coutures chunk** : la deformation doit etre identique des deux cotes d'une frontiere
|
||
|
|
de chunk. Le hash base sur la position monde (pas locale) garantit la coherence.
|
||
|
|
- **Animation** : en mode animation (terrain regenere a 30Hz), la deformation doit etre
|
||
|
|
stable. Utiliser la position entiere (pas le centroide) comme seed du hash.
|
||
|
|
|
||
|
|
## Estimation d'effort
|
||
|
|
|
||
|
|
- Etendre MaterialDesc : 15 min
|
||
|
|
- Fonction deformVertex : 30 min
|
||
|
|
- Integration dans meshSurfaceNets : 30 min
|
||
|
|
- Tuning des parametres par materiau : 1h
|
||
|
|
- **Total : ~2h**
|
||
|
|
|
||
|
|
Aucun changement shader, aucun changement GPU buffer, aucun impact performance.
|