The atlas grows in pages
Until now the lightmap was one texture, and a bigger level simply meant coarser shadows — the same 2048 px stretched over more square meters. That is backwards: texel density is the quality setting, and the atlas should grow to hold it.
So it does. The Lightmap menu now offers:
- Texel density (texels per world unit) — the real quality knob, with the centimeter equivalent spelled out under it (
1 texel = 5.0 cm). Set it to 0 for the old behavior: everything squeezed into a single page.
- Page size — how big one page is (256…4096 px). The atlas adds pages as needed to keep the density.
- A page estimate before you bake:
~12 pages (48 MB), turning orange past 32 pages, so a density that would cost hours is visible up front. Generating the atlas once calibrates the estimate against the real unwrap instead of the theoretical maximum.
Everything downstream followed: the baker outputs one image per page, the preview thumbnail and the full-size viewer got a page stepper, Save PNG names the page, the OBJ atlas export rasterizes per page, and the export bundle ships <map>_lightmap_0.png, _1.png, … next to the map.
Big flat surfaces no longer lose their detail
A subtle one that cost real quality: xatlas merges coplanar faces into a single planar chart, and silently scales down anything that will not fit a page — measured at 66% of the requested density on a 200×200 m floor. Cell ids fed as per-face materials break those charts up, but only if the geometry is actually cut at the cell borders.
So there is now a grid clipper: triangles are cut along a global cell grid, so two triangles sharing an edge are cut at the same point on it and the surface stays watertight. (Bisecting each triangle on its own terms would leave T-vertices, which show up in the bake as black seams along every cut.) Covered by its own test.
Per-object lightmap density
New Inspector slider under Material: Density, a multiplier on the scene's texel density for that object (Unity calls it Scale In Lightmap). Pages are the budget — spend texels on the feature wall the player studies, save them on the ceiling. It rides through the CSG boolean the same way per-face textures do, so a cut surface inherits the cutter's density.
The bake finally tells you what it did
The Lightmap panel stopped being optimistic:
Tris: 8412 of 8590 atlased— a gap means xatlas rejected faces (degenerate or duplicate); those surfaces carry no lightmap at all.
Density: 6.2 of 8.0 tx/unit (78%)— what the atlas actually delivers versus what you asked for.
3 charts shrunk to fit a page— the surfaces that lost detail, with the hint that a bigger page size keeps them intact.
- A warning when the mesh hits the vertex cap.
Indirect Only
New bake mode: bake ambient and bounced light only and leave the direct term to the engine's realtime lights. Indirect light is soft, so it needs far less texel density — a much cheaper bake for a game that lights itself in realtime.
The editor's baked view adds a stand-in direct term on top, otherwise the preview would be near-black and tell you nothing. The Unity importer knows about it too: the package's Baked Lit shader replaces lighting and takes no realtime light, so an indirect-only map is imported with plain lit materials + realtime lights, and the import log explains why.
GPU bake
The whole ray-tracing part of the bake now runs in a compute shader (lightmap.comp), one invocation per texel — same math as the CPU worker. Only the tracing moved; the atlas rasterization, the BVH build and the final dilate stay on the CPU, where they are cheap.
The interesting part is how it runs. A bake is not one dispatch — it is a state machine stepped once per frame from the main thread (a Vulkan queue may only be used from one thread at a time, and the renderer owns it). Each step submits one batch of texels and returns; the next frame polls the fence. That keeps the editor drawing, keeps progress and cancel working, and keeps every submit far below the OS GPU watchdog timeout — the one that resets your GPU after ~2 seconds and takes the whole application with it. The batch size adapts to the measured time per batch.
Two command buffers, not one: with a single slot the GPU sat idle from the moment a batch finished until the next frame noticed, and at 120 fps that idle time dominated enough to make the "GPU" bake slower than the CPU one.
Both devices now share one bake core, so they cannot drift apart on what a texel is — and that shared BVH got a binned SAH split instead of the old median split (2–3× fewer node visits per ray; the build itself is milliseconds either way) plus triangles reordered into leaf order for the cache.
Pick the device in Lightmap → Device. Anything that stops the GPU path (no compute, does not fit in VRAM) falls back to the CPU silently and says why right there, and the menu reports Last bake: 12.4 s on the GPU. Two new test harnesses came with it: a correctness test (CPU vs GPU on small scenes) and a benchmark on a scene with a genuinely deep BVH, printing rays/second for both — so a change can be judged instead of guessed at.
Unity package 0.2.0
Kept in step with all of the above: multi-page lightmaps (one material per texture × page, so every surface samples the page its UV1 belongs to), the per-page PNG sidecars, indirect-only handling with a proper import warning, and an importer version bump so existing maps re-import cleanly.