|
Vulkan Schnee 0.0.1
High-performance rendering engine
|
Lightmaps capture baked direct and indirect illumination for static scene geometry. At runtime, the staticLightmap shader samples a pre-baked EXR texture via a dedicated UV channel (TEXCOORD_1), replacing dynamic lighting with zero-cost precomputed results.
The pipeline: configure objects in Blender → bake with Cycles → export via glTF → load in engine.
Source files:
Only static mesh objects participate in lightmap baking. Objects with is_static = False are hidden during bake and skipped entirely by the bake script.
Materials must use the VS Static Lightmap shader node. Add it from Shader Editor > Sidebar (N) > Vulkan Schnee > Lightmap Shaders > Static Lightmap (UV2).
For each static mesh:
Or use the automated operator to handle all three steps at once (see Automated Setup).
Add the VS Static Lightmap shader node to every material on lightmapped objects:
Connect inputs:
Connect the node's BSDF output to Material Output.
Go to Properties > Scene Properties > Lightmap Settings:
| Setting | Description |
|---|---|
| Quality Preset | Draft (32), Preview (128), Production (512), Ultra (1024), Custom |
| Bake Samples | Sample count; only editable when preset is Custom |
| Adaptive Sampling | Stops sampling converged pixels; reduces bake time |
| Noise Threshold | Lower = cleaner (0.01 high quality, 0.1 fast draft) |
| Min Samples | Minimum samples before adaptive kicks in |
| Use Denoiser | Post-bake denoising (OptiX for NVIDIA, OIDN for any GPU) |
| UV Margin | Pixels around UV islands to prevent seams (default 6) |
| Margin Type | Adjacent Faces (recommended) or Extend |
| EXR Compression | None (fastest), ZIP (smallest) |
The script bakes all objects that have has_lightmaps = True and a valid LightmapUV channel. Output files go to //lightmaps/ relative to the .blend file.
Bake output naming:
After baking, each object gets a duplicate in the Baked_Lightmaps collection (hidden from render). The original object's lightmap_path property is updated to the output file path.
Bake optimization: The script uses a single-BVH strategy: all objects are prepared first (images created, nodes set up), baked in one pass without scene modifications, then finalized. This avoids repeated BVH rebuilds that would otherwise dominate bake time for large scenes.
Use File > Export > GLTF with Vulkan Schnee Data. The exporter writes lightmap metadata to the VULKAN_SCHNEE_engine_properties node-level extension:
In Object Properties > Vulkan Schnee Engine > Lightmaps, preset buttons set resolution directly:
| Preset | Use |
|---|---|
| 512 | Small/background objects |
| 1K (1024) | Standard resolution |
| 2K (2048) | Large/hero objects |
| 4K (4096) | Maximum detail |
The resolution calculator assigns resolution based on world-space surface area × texel density:
Result is clamped to the nearest power-of-2 in [256, 512, 1024, 2048, 4096].
Texel density presets (texels per linear meter):
| Preset | Density | Application |
|---|---|---|
| Low | 128 | Mobile/background |
| Medium | 256 | Standard quality (default) |
| High | 512 | Hero objects |
| Ultra | 1024 | Extreme detail |
Run auto-assignment from Scene Properties > Lightmap Settings > Resolution Calculator:
Operator: object.vulkanschnee_automated_lightmap_setup
Location: 3D Viewport > Sidebar (N) > Schnee Engine > Automated Lightmap Setup
Processes selected mesh objects in one step: creates the LightmapUV UV channel, unwraps it, and sets the object properties.
Dialog options:
| Option | Description |
|---|---|
| Resolution Preset | 512, 1024, 2048, 4096, or Custom |
| Enable Lightmaps | Sets has_lightmaps = True |
| Lightmap Only | Enables light-only baking (pure lighting, no albedo) |
| Use Existing UV | Skips channel creation if LightmapUV already exists |
| Use xatlas Packing | Cross-platform UV island packing using the bundled xatlas CLI |
| UV Padding (px) | Pixel padding between UV islands |
| High Quality Packing | Uses xatlas brute-force chart placement for better utilization |
| Angle Limit | Smart UV unwrap angle limit (default ~66°) |
| Initial Island Margin | Temporary Smart UV Project margin before xatlas packing |
Processing order per object:
The xatlas packer is a small native tool built on demand by CMake from https://github.com/jpcy/xatlas. Objects are grouped by lightmap resolution for the command call, but each mesh is packed into its own 0-1 UV space so existing per-object lightmap baking still works.
When bake_light_only = True on an object, the bake uses DIFFUSE pass type with direct + indirect contributions and no albedo color. This produces pure lighting data without material colors — useful for scenes where albedo is applied separately or where tonemapping is not desired.
The engine applies this texture directly, without the albedo multiplication that the standard combined-mode path uses.
For scenes with many objects (100+), repeated Cycles BVH rebuilds dominate bake time. Atlas batching groups objects and bakes them together into a shared texture atlas, reducing BVH rebuilds from N to N/groupSize.
Enable in Scene Properties > Lightmap Settings > Atlas Batching:
| Setting | Description |
|---|---|
| Enable Atlas Batching | Groups objects by resolution for batch baking |
| Max Atlas Size | 4K (4096), 8K (8192, recommended), 16K (16384) |
Objects per atlas at 8K:
| Object Resolution | Objects per Atlas |
|---|---|
| 256px | 1024 |
| 512px | 256 |
| 1024px | 64 |
| 2048px | 16 |
| 4096px | 4 |
Strategy: Objects are grouped by resolution. Within each group, duplicates are joined into one mesh, baked as a single atlas, then results are split back per-object. Original objects are never modified.
Expected speedup: 40–100x for BVH-bound scenarios with 100+ objects at similar resolutions.
The resolution overlay color-codes objects by their assigned lightmap resolution. Toggle via Scene Properties > Lightmap Settings > Visualization > Show Resolution Overlay.
| Color | Resolution |
|---|---|
| Red | 256px |
| Orange | 512px |
| Yellow | 1024px |
| Green | 2048px |
| Blue | 4096px |
Objects without lightmaps are not drawn. The overlay caches per-object GPU batches and invalidates them when mesh geometry changes.
The engine loads lightmap textures during glTF import in GltfLoader.cpp. For each mesh with lightingProperties.hasLightmaps = true and a valid lightmapProperties.lightmapPath:
The GPU material struct (GpuPbrMaterial) has two lightmap fields:
The fragment shader (triangle_static_lightmap.frag) multiplies albedo by the lightmap sample:
If no lightmap is present (hasLightmap == 0), the shader falls back to ambient_term (defaults to 1.0 for static lightmap materials, so the object appears fully lit rather than black).
The static lightmap shader supports debug visualization via the Debug Mode material parameter:
| Mode | Output |
|---|---|
| 0 | Normal rendering |
| 1 | Surface normals as RGB |
| 2 | Lightmap UV coordinates |
| 3 | Raw lightmap without albedo |
| 4 | Albedo only (no lighting) |
| 5 | Normal map texture (raw RGB) |
| 6 | Metallic-roughness texture (R=metallic, G=roughness) |
| 7 | Emissive texture |
| 8 | Texture indices as colors (R=baseColor, G=normal, B=lightmap) |
| 9 | Texture binding presence (binary: present=1, missing=0) |
| 10 | Meshlet ID (PCG hash coloring) |
| 11 | Meshlet ID (golden ratio hue separation) |
| 12 | Object ID visualization |
Diagnose Lightmap Issues (3D Viewport > Sidebar > Schnee Engine > Diagnose Lightmap Issues):
Validate Lightmaps (object.vulkanschnee_validate_lightmaps):
Object Properties panel warnings:
The exporter writes two keys to VULKAN_SCHNEE_engine_properties per node:
lightingProperties (inside lightingProperties sub-object):
lightmapProperties (sibling of lightingProperties):
The engine parser (GltfLoader.cpp) reads both keys independently. lightmapProperties is only loaded if hasLightmaps = true and lightmapPath is non-empty.