bvle-voxels/CLAUDE.md
Samuel Bouchet 4419c612bd Phase 8: Real stylized textures with UDN triplanar normal mapping
- Load CC0 FreeStylized textures (6 materials: grass, dirt, stone, sand, snow, smoothstone)
  as Texture2DArray: t1=albedo+heightmap RGBA, t7=normal maps GL format
- Height-based texture blending: winner-takes-all with sharpness=16, 40% blend zone,
  asymmetric bias (coeff 1.6) for resistBleed materials (grass resists sand bleed)
- UDN triplanar normal mapping with 3 critical fixes:
  * Use raw normal (NOT abs) in UDN formula — abs inverts lighting on -X/-Y/-Z faces
  * sign(normal) correction on tangent X for back-facing UV mirror
  * GL green channel flip on Y-projection only (not X/Z where V=worldY is correct)
- Dirt material rendered smooth (FLAG_SMOOTH), ground_02 texture darkened 0.75
- Sun orbit debug mode (F7): 10s cycle with sinusoidal altitude
- Crosshair + face debug HUD (F8): DDA raycast, camera/target/face/normal info
- Screenshot F6 now writes companion .log file with full debug state
- Document UDN pitfalls and logical vs physical coordinates in TROUBLESHOOTING.md
- Add tools/prepare_textures.py for texture pipeline (ZIP → albedo+height RGBA + normal)
2026-04-01 13:41:06 +02:00

13 KiB
Raw Blame History

BVLE Voxels - Prototype de Moteur Voxel Hybride

Vue d'ensemble

Prototype de moteur voxel basé sur Wicked Engine (MIT, C++17, DX12/Vulkan) pour valider les performances de rendu sur GPU moderne (AMD RDNA 2+ / Nvidia RTX 3060+). Le document de spécification complet est dans voxel_engine_spec.md à la racine du projet.

Cible : 60+ fps en 1440p, monde de 512x512x256 voxels visibles.

Architecture

bvle-voxels/
├── CMakeLists.txt              # Build CMake racine
├── engine/                     # Wicked Engine (clone --depth 1, branche main)
│   └── WickedEngine/shaders/voxel/  # Nos shaders copiés ici pour compilation DXC
├── src/
│   ├── voxel/                  # Bibliothèque VoxelEngine (static lib)
│   │   ├── VoxelTypes.h        # Types fondamentaux (VoxelData, PackedQuad, MaterialDesc, ChunkPos)
│   │   ├── VoxelWorld.h/.cpp   # Monde voxel (hashmap de chunks, génération procédurale)
│   │   ├── VoxelMesher.h/.cpp  # Binary Greedy Mesher CPU + SmoothMesher (Naive Surface Nets)
│   │   ├── VoxelRenderer.h/.cpp# Renderer + VoxelRenderPath (CameraController, AnimationState, VoxelProfiler)
│   │   ├── VoxelRTManager.h/.cpp # Ray tracing: BLAS/TLAS lifecycle, shadows+AO dispatches
│   │   ├── DeferredGPUBuffer.h # Utilitaire staging→dirty→capacity GPU buffer upload
│   │   └── TopingSystem.h/.cpp # Système de topings (biseaux décoratifs sur faces +Y)
│   └── app/
│       └── main.cpp            # Point d'entrée Win32 + crash handler SEH
├── shaders/                    # Sources HLSL des shaders voxel (copiés dans engine/ au build)
│   ├── voxelCommon.hlsli       # Root signature et CB partagés (inclus par tous les shaders)
│   ├── voxelVS.hlsl            # Vertex shader (vertex pulling, triple-mode: CPU/MDI/GPU mesh)
│   ├── voxelPS.hlsl            # Pixel shader (triplanar + lighting)
│   ├── voxelCullCS.hlsl        # Compute shader frustum+backface cull (Phase 2.3)
│   ├── voxelMeshCS.hlsl        # Compute shader GPU mesher 1×1 (Phase 2.4-2.5)
│   ├── voxelTopingVS.hlsl      # Vertex shader topings (instanced vertex pulling, t4/t5)
│   ├── voxelTopingPS.hlsl      # Pixel shader topings (triplanar + directional lighting)
│   ├── voxelSmoothVS.hlsl      # Vertex shader smooth Surface Nets (vertex pulling, t6)
│   ├── voxelSmoothPS.hlsl      # Pixel shader smooth (triplanar + material blending)
│   ├── voxelBLASExtractCS.hlsl # Compute shader BLAS position extraction (Phase 6.1)
│   ├── voxelShadowCS.hlsl     # Compute shader RT shadows + raw AO (inline ray queries, Phase 6.2+6.3)
│   ├── voxelAOBlurCS.hlsl     # Compute shader bilateral AO blur (separable H/V, Phase 6.3)
│   └── voxelAOApplyCS.hlsl    # Compute shader AO apply + tone mapping + saturation (Phase 6.3 + 7)
├── assets/
│   ├── voxel/                  # Textures stylisées (6 albedo+height RGBA + 6 normal GL, 512x512)
│   └── raw/                    # ZIPs sources FreeStylized.com (CC0)
├── tools/
│   └── prepare_textures.py     # Script: ZIP → albedo+heightmap RGBA + normal PNG (512x512)
├── CLAUDE.md
└── TROUBLESHOOTING.md          # Pièges techniques, debugging, APIs Wicked

Build

Prérequis

  • CMake 3.19+ (winget install Kitware.CMake)
  • Visual Studio 2022 Build Tools (winget install Microsoft.VisualStudio.2022.BuildTools)
  • Windows SDK 10.0.26100+ (winget install Microsoft.WindowsSDK.10.0.26100)

Commandes

# Configurer (depuis la racine du projet)
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_VERSION=10.0.26100.0

# Compiler
cmake --build build --config Release --target BVLEVoxels --parallel

# Exécutable produit dans build/Release/BVLEVoxels.exe

Le SDK 10.0.26100 est requis car les headers DX12 (d3dx12_check_feature_support.h) fournis par Wicked Engine ne sont pas compatibles avec le SDK 22621.

Exécution

IMPORTANT : Le CWD doit être la racine du projet, pas build/Release/. L'exe utilise des chemins relatifs pour les assets (Content/) et la compilation shader (engine/WickedEngine/shaders/).

# Lancer normalement (fenêtre 1920x1080 centrée)
build/Release/BVLEVoxels.exe

# Mode screenshot (640x480, capture 3 vues, quitte automatiquement)
build/Release/BVLEVoxels.exe screenshot

# Autres arguments
build/Release/BVLEVoxels.exe debug         # Faces colorées par direction
build/Release/BVLEVoxels.exe debugsmooth   # Scène smooth debug
build/Release/BVLEVoxels.exe vulkan        # Forcer backend Vulkan

Fichiers de sortie (écrits dans le CWD, donc la racine du projet) :

  • bvle_backlog.txt — log Wicked Engine
  • bvle_crash.log + bvle_crash.dmp — crash report SEH (si crash)
  • bvle_screenshot_*.png — captures mode screenshot ou F6

Raccourcis clavier :

  • F2 — toggle backlog Wicked
  • F3 — toggle animation terrain (30 Hz)
  • F4 — toggle debug blend
  • F5 — cycle RT shadows/AO (ON → debug shadows → debug AO → OFF)
  • F6 — screenshot in-app (sauvegarde voxelRT_ en PNG + .log compagnon)
  • F7 — toggle sun orbit (cycle 10s, altitude sinusoïdale)
  • F8 — toggle crosshair + debug face info (camera, target, face, normal map proj)

Post-build automatique (CMakeLists.txt)

Le build copie automatiquement :

  1. dxcompiler.dll → à côté de l'exe (requis pour la compilation runtime des shaders)
  2. shaders/*.hlslengine/WickedEngine/shaders/voxel/ (pour que LoadShader les trouve via SHADERSOURCEPATH)
  3. engine/Content/ → à côté de l'exe (assets Wicked Engine)

Intégration Wicked Engine

Backend graphique

Wicked Engine utilise DX12 par défaut sur Windows, Vulkan sur Linux. Les shaders sont écrits en HLSL et compilés via DXC. Pour forcer Vulkan sur Windows, passer "vulkan" en argument de ligne de commande.

Point d'entrée et architecture de rendu

VoxelRenderPath hérite de wi::RenderPath3D. Le rendu voxel utilise ses propres render targets (voxelRT_, voxelDepth_) et est exécuté dans Render() sur un command list dédié. Le résultat est composité dans Compose() via wi::image::Draw().

NE JAMAIS créer un render pass dans Compose() : cette méthode est appelée à l'intérieur du render pass du swapchain. Imbriquer des render passes est interdit en D3D12.

Render()  → RenderPath3D::Render()     // Wicked rend sa scène
          → device->BeginCommandList() // Nouveau cmd list
          → renderer.render(cmd, ...)  // Notre render pass (clear + draw voxels → voxelRT_)
Compose() → RenderPath3D::Compose()    // Wicked affiche son résultat
          → wi::image::Draw(voxelRT_)  // On overlay nos voxels par-dessus

La caméra est gérée manuellement dans Update() en écrivant directement camera->Eye, camera->At (direction LookTo), camera->Up.

See TROUBLESHOOTING.md for the detailed Wicked API reference table, shader binding pitfalls, DX12 resource state management, and debugging guides.

Détails d'implémentation

VoxelData (16 bits)

[15:8] material ID (256 matériaux)
[7:4]  flags (smooth, transparent, emissive, custom)
[3:0]  metadata (orientation, variant)

PackedQuad (64 bits = 8 octets par quad)

[5:0]   position X (0-63)       [23:18] width (1-32)
[11:6]  position Y (0-63)       [29:24] height (1-32)
[17:12] position Z (0-63)       [32:30] face (0-5)
[40:33] material ID              [48:41] blendMatID
[59:49] chunkIndex (11 bits)     [63:60] blendEdges (4 bits)

Binary Greedy Mesher (CPU, VoxelMesher.cpp)

Masques binaires par axe, face culling par shift/XOR, greedy merge rectangulaire par tranche de profondeur.

Génération procédurale (VoxelWorld.cpp)

Perlin noise 3D, fBm 5 octaves (2 en animation), caves 3D, matériaux par altitude. Chunks Y=0..7. Animation 60 Hz via regenerateAnimated() parallélisé avec wi::jobsystem.

Renderer (VoxelRenderer.cpp)

  • Triple-mode VS : CPU path, MDI path, GPU mesh path (défaut)
  • GPU mesh : compute shader voxelMeshCS → barrier UAV→SRV → DrawInstanced (readback 1-frame-delay)
  • Vertex pulling via SV_VertexID, pas de vertex buffer classique
  • Per-chunk info : StructuredBuffer<GPUChunkInfo> (80 bytes/chunk)
  • Height-based blending (Phase 3) : PS lit voxelDataBuffer (t3), winner-takes-all heightmap, corner attenuation
  • Render targets propres : voxelRT_ (R8G8B8A8) + voxelDepth_ (D32_FLOAT)
  • CPU profiling : VoxelProfiler (21 ProfileAccum, moyennes toutes les 5s)
  • DeferredGPUBuffer : utilitaire pour buffers GPU avec staging CPU, dirty flag, capacity-based growth (25% headroom)
  • VoxelRTManager (VoxelRTManager.h/.cpp) : gère BLAS/TLAS, dispatches RT shadows+AO, isolé du renderer
  • VoxelRenderPath décomposé en : CameraController (mouvement/souris), AnimationState (tick terrain), VoxelProfiler
  • Toping sort : counting sort O(n) par (type, variant) au lieu de std::sort

Phases de développement

Phase 1 - Setup et meshing de base [FAIT]

Fork Wicked Engine, VoxelWorld procédural, Binary Greedy Mesher CPU (~300K quads), rendu vertex pulling, caméra libre, crash handler SEH.

Phase 2 - Performance GPU [FAIT]

  • 2.1 : Mega-buffer + CPU frustum/backface cull + per-face DrawInstanced
  • 2.2 : CPU-filled indirect args + DrawInstancedIndirectCount (MDI)
  • 2.3 : GPU compute culling (0.006 ms / 168 chunks)
  • 2.4 : GPU compute mesher benchmark (CPU 277ms vs GPU 5.3ms)
  • 2.5 : GPU meshing production + CPU optimisations (fused regen+pack, memcpy, dirty cache)
  • Résultat : 80-110 FPS avec animation 60 Hz, 700+ FPS statique

Phase 3 - Texture blending [FAIT]

PS-based heightmap blending, winner-takes-all, corner attenuation subtractive. GPU mesh path uniquement.

Phase 4 - Toping [EN COURS]

  • 4.1 [FAIT] : TopingSystem infrastructure, 4-bit adjacency, priority-based, stone wedges + grass tufts
  • 4.2 [FAIT] : Shaders dédiés, vertex pulling instancié, half-Lambert + translucency vegetation
  • 4.3 [A FAIRE] : Plus de types, LOD, animation vent, compute shader instances

Phase 5 - Rendu smooth [EN COURS]

  • 5.1 [FAIT] : Naive Surface Nets CPU, SDF binaire, cross-chunk connectivity, smooth/blocky boundary
  • 5.2 [FAIT] : Smooth vertex normals, geometric normals triplanar, optimisations CPU (560ms → 17ms)
  • 5.3 [A FAIRE] : GPU compute Surface Nets
  • 5.4 [A FAIRE] : SDF lissé, LOD, pipeline asynchrone

Phase 6 - Ray tracing hybride [EN COURS]

  • 6.1 [FAIT] : Normal RT (MRT), BLAS extraction CS, 3 BLAS (blocky+smooth+topings), TLAS
  • 6.2 [FAIT] : RT shadows (3 jittered rays, TMin adaptatif, colored shadows)
  • 6.3 [FAIT] : RT AO (4 cosine-weighted rays, IGN + Cranley-Patterson, temporal accumulation, bilateral blur)
  • 6.4 [A FAIRE] : Fallback shadow maps + SSAO

Phase 7 - Stylized Lighting [EN COURS]

  • 7.1 [FAIT] : Hemisphere ambient, colored shadows, rim light, tone mapping + saturation, screenshot mode

Phase 8 - Textures stylisées réelles [EN COURS]

  • 8.1 [FAIT] : Chargement textures CC0 FreeStylized (6 matériaux, albedo+heightmap RGBA, normal maps GL)
  • 8.2 [FAIT] : Texture2DArray (t1=albedo+height, t7=normals), triplanar sampling, stb_image loading
  • 8.3 [FAIT] : Height-based texture blending (winner-takes-all, sharpness=16, corner attenuation)
  • 8.4 [FAIT] : Asymmetric blend pour resistBleed (coeff 1.6), zone de blend 40%
  • 8.5 [FAIT] : UDN triplanar normal mapping (sign correction, GL green flip Y-proj only, NO abs)
  • 8.6 [FAIT] : Dirt rendu smooth (FLAG_SMOOTH), ground_02 texture assombrie 0.75
  • 8.7 [FAIT] : Sun orbit debug (F7, cycle 10s), crosshair + face debug HUD (F8)
  • 8.8 [FAIT] : Screenshot F6 avec .log compagnon (camera, target, debug states, RT stats)

Métriques cibles et résultats

Métrique Cible Résultat (Ryzen 7 9800X3D + RX 9070 XT)
FPS 1440p > 60 fps 80-110 FPS (anim blocky), 700+ FPS (statique)
FPS anim smooth+topings > 15 fps 17 FPS (smooth+topings+blocky anim 60Hz)
Meshing GPU (blocky) < 200 us/chunk ~0.6 us/chunk (0.1ms / 171 chunks)
Meshing CPU (smooth) < 30ms 17ms (parallele, 648 chunks)
Memoire GPU < 500 Mo ~30 Mo
Draw calls < 100 1 (GPU mesh) ou 1 (MDI)

Conventions

  • Namespaces : tout le code voxel est dans namespace voxel
  • Chunks : 32x32x32, configurable via CHUNK_SIZE
  • Coordonnées : Y = haut, monde infini en X/Z, hashmap sparse
  • Matériaux : palette de 256, index 0 = air (vide), 1=grass, 2=dirt, 3=stone (blocky), 4=sand, 5=snow (smooth), 6=smoothstone (smooth)
  • Faces : 0=+X, 1=-X, 2=+Y, 3=-Y, 4=+Z, 5=-Z
  • Smooth flag : FLAG_SMOOTH = 0x1 dans VoxelData flags — active Surface Nets au lieu du rendu blocky