|
Vulkan Schnee 0.0.1
High-performance rendering engine
|
SH probes capture indirect diffuse lighting at discrete points in a scene. At runtime, each object samples the nearest probe to receive ambient illumination from surrounding surfaces, lights, and the sky.
The pipeline has three stages: bake in Blender → export via glTF → load in the engine.
Source files:
In the Blender Outliner, create a collection named exactly ProbeVolumes. Add one or more closed mesh objects to this collection. Each mesh defines a region where probes will be placed.
The volume meshes do not render in the final export. Any mesh topology works as long as the volume is closed (watertight normals pointing outward).
Multiple meshes in the collection are treated as separate volumes — probes are placed inside the union of all volumes.
The baking script places probes only at positions that satisfy both conditions:
Probes inside solid geometry are pruned automatically. If a probe location falls inside a wall due to cell alignment, it is skipped.
Click Bake SH in Volumes in the 3D Viewport > Sidebar (N) > Schnee Engine panel. A dialog appears with these parameters:
| Parameter | Default | Description |
|---|---|---|
| Probe Volume Collection | ProbeVolumes | Name of the Blender collection containing volume meshes |
| Min Cell Size (m) | 0.5 | Smallest octree cell size. Cells this small are placed near scene geometry. |
| Max Cell Size (m) | 16.0 | Largest octree cell size. Open areas (no nearby geometry) get one probe per 16m cell. |
| Panorama Resolution | 128 | Width of each per-probe equirectangular render. Height = Width / 2. |
| Max SH Band (L) | 2 | Maximum SH band. L=2 produces 9 coefficients (L0 + L1 + L2). |
The baking system builds a sparse adaptive octree over the probe volumes. Cells that overlap scene geometry are subdivided down to Min Cell Size. Cells with no nearby geometry remain at Max Cell Size. This concentrates probes where lighting varies (near surfaces, in corners) and uses fewer probes in open space.
Example for a 10m × 10m × 4m room:
The operator temporarily overrides Cycles render settings for each probe:
Original scene render settings are restored after baking completes.
SH baking requires scipy. Install it from the Schnee Engine panel:
Or manually in Blender's Python environment:
The operator renders one Cycles panorama per probe location and projects the result onto L0–L2 SH basis functions. Progress is printed to the system console (Window > Toggle System Console).
Output: //sh_coeffs/probes.shprobe (relative to the .blend file)
For a scene with multiple named volumes, only one probes.shprobe is written per bake run. To bake separate volumes independently, run the operator multiple times with different collection names and manually rename the output files between runs.
Debug: Bake SH at Cursor bakes a single probe at the 3D cursor position. Output goes to //sh_coeffs/debug_probe.txt (text) and //sh_coeffs/probes.shprobe (binary, single probe). Useful for verifying that the render pipeline and scipy integration work before running a full bake.
The glTF exporter plugin automatically includes .shprobe files during export — no manual configuration is needed. In File > Export > glTF 2.0, ensure Export Engine Properties is enabled (it is by default).
At export time, the gather_scene_hook scans sh_coeffs/ next to the .blend file. For each .shprobe file found, it:
The scene-level extension looks like this for a single volume:
gridOrigin and grid axes are in engine coordinate space (Y-up). The baking script converts from Blender's Z-up coordinate system automatically:
Place multiple .shprobe files in sh_coeffs/. The exporter adds one entry per file to shProbeGrids, sorted by filename. The engine loads all entries on scene load and queries them in order — the first grid containing the world position wins.
After export, the directory structure next to the .gltf file:
When a glTF scene is loaded, FrameProcessing.cpp checks the scene-level VULKAN_SCHNEE_engine_properties extension for a shProbeGrids array. Each entry is parsed into an SHProbeGridInfo struct and the referenced .shprobe file is loaded via SHProbeGrid::loadFromFile().
Probe data is uploaded to a GPU storage buffer (SHProbeBuffer). Index 0 is always the neutral fallback probe (hardcoded SH coefficients matching the shader defaults). Objects outside all probe volumes receive this fallback.
Per-object probe assignment uses RenderingDataManager::queryProbeIndex(worldPos), which iterates loaded grids and calls SHProbeGrid::getProbeIndex() on each. The first grid returning a valid index (≥ 0) wins. Invalid occupancy grid cells (value -1) also fall back to index 0.
Version 1 format, little-endian:
| Offset | Size | Type | Field |
|---|---|---|---|
| 0 | 4 | uint32 | Magic: 0x52485053 ("SHPR") |
| 4 | 4 | uint32 | Version: 1 |
| 8 | 4 | uint32 | Probe count |
| 12 | 4 | uint32 | Grid dim X |
| 16 | 4 | uint32 | Grid dim Y |
| 20 | 4 | uint32 | Grid dim Z |
| 24 | 4 | float | Grid origin X |
| 28 | 4 | float | Grid origin Y |
| 32 | 4 | float | Grid origin Z |
| 36 | 4 | float | Grid spacing (meters) |
| 40 | 4 | float | Default SH scale |
| 44 | 4 | float | Default ambient term |
| 48 | probeCount × 160 | - | Probe data (9 × vec4 coefficients + scale + ambient + 8 bytes padding) |
| after probes | dimX × dimY × dimZ × 4 | int32[] | Occupancy grid (-1 = empty cell) |
Occupancy grid flat index: x + y * dimX + z * dimX * dimY
The VS Movable Diffuse shader node in Blender has two parameters that control how SH data is applied at runtime. Set them per-material in the shader node graph:
| Parameter | Default | Description |
|---|---|---|
| SH Scale | 1.0 | Multiplier for all SH coefficients. Lower it only for intentional artistic dimming. |
| Ambient Term | 0.1 | Constant added to diffuse after SH evaluation. Prevents fully dark areas in uncovered regions. |
These values are exported with the material via the VULKAN_SCHNEE_materials extension and read by the engine shader pipeline.
Interior room (living room, corridor):
Outdoor environment (courtyard, terrain):
Large interior with open areas (warehouse, hall):
Increasing Panorama Resolution beyond 128 has diminishing returns — SH order 2 captures only low-frequency lighting, so detail in the panorama averages out during projection. Use higher resolution only when L_MAX is set to 3 or 4.