Builtin Library
README.md
baking
Shared utilities for the globalIllumination package's lightmap and probe
bakers — light gathering, surface-attribute gathering (albedo + emissive
per instance), transform helpers, and the scene-geometry gather that
feeds compute.buildBvh. The lowest layer of the package; both bakers
consume the same primitives so the lighting model stays consistent
across the lightmap and probe pipelines.
Exports
Constants:
-
Baking.SH_COEFFS = 9— number of L2 SH basis functions per probe. -
Baking.SH_FLOATS_PER_PROBE = 36— std430-padded SH stride. -
Baking.TRI_FLOATS = 72— std430-padded triangle stride (matches the BVH builder). -
Baking.BVH_NODE_FLOATS = 8— floats per BVH node on the GPU. -
Baking.LIGHT_FLOATS = 16— GPU light record stride. -
Baking.RAY_BUDGET_PER_FRAME = 1048576— estimated ray traversals of bake work one frame carries. -
Baking.PER_THREAD_RAY_CAP = 1024— rays one invocation traces in one sample pass. -
Baking.OCCUPANCY_TARGET = 16384— invocations per dispatch the plan aims for. -
Baking.MAX_FRAMES_IN_FLIGHT = 2— fenced frames of bake work queued on the GPU at once. -
Baking.LIGHTMAP_BYTES_PER_TEXEL = 144— GPU bytes one lightmap texel holds while its receiver bakes.
Functions:
Baking.vec3 / add / sub / scale / dot / cross / length / normalize— vec3 helpers used everywhere downstream.Baking.lightsTraced(lights) -> { direct, path }/Baking.lightmapRays(spec)/Baking.probeRays(spec)— the cost model: estimated rays per texel or probe.Baking.planChunks(spec) -> { Chunk }— split a bake into dispatches that each stay inside a ray budget.Baking.bakeLimits(pageSize) -> BakeLimits— the memory budget and the largest lightmap resolution this device bakes.Baking.newGpuScheduler(opts) -> GpuScheduler— issue a pass's dispatches a frame's worth at a time, fenced, checking the device. A wait for one drawn frame is bounded to 30 seconds of real time (frameWaitSeconds); a renderer that draws none in that time raises naming the device's state.Baking.beginGpuPass(programs) / Baking.gpuFailureCause(start) -> string— say why queued GPU work did not produce its result: a lost device, a dispatch that failed since the pass began, an error the device reported.Baking.gatherLights() -> { Light }— enumerate everyLightcomponent in the scene.Baking.packLights(lights) -> FloatArray— pack lights for compute upload.Baking.transformPoint / transformNormal / makeMatrix / entityWorldMatrix— 4x4 matrix helpers.Baking.surfaceAttributes(entityId, cache) -> { albedo, emissive }— the entity's bake-facing surface description: linear albedo (base_color factor × average base-color texture color, clamped below 1) and emissive (color × strength).Baking.gatherMeshInstances(excludeIds?) -> { instances, release }— every mesh-bearing entity as a{ guid, transform, attributes }record forcompute.buildBvh, CPU mesh items materialised;release()drops them again.Baking.getEntityTriangles(entityId) -> { Triangle }— one entity's mesh triangles in local space (used by the UV rasterizer).
Types:
Vec3 = { x: number, y: number, z: number }ColorRGB = { r: number, g: number, b: number }UV = { x: number, y: number }Mat4 = { number }— 16-float row-major.Light = { id, kind, position, direction, color, intensity, radius }Triangle = { v0, v1, v2, n0, n1, n2, uv0, uv1, uv2 }
Usage
local Baking = require("@builtin::systems.globalIllumination.baking")
local lights = Baking.gatherLights()
local gather = Baking.gatherMeshInstances({ entityId })
local bvh = compute.buildBvh(gather.instances)
gather.release()
Notes
- The cost model counts
lightmapRays:17 + 64 * direct + 64 (contact occlusion)rays per texel in the direct stage, thenbounces * (1 + path)per chain;probeRayscountsbounces * (1 + path)per sample pluspath + 6in the last pass. It is an upper bound: a path that ends early still counts. - A scheduler's
gatewaits for the pass's programs to compile (a dispatch queued behind a compile drains with every other held dispatch in one frame),issue(rays, fn)runsfninside the frame's budget,finishwaits for the last fence,checkraises when the device was lost or replaced,closefrees the fence buffer, andstatsreports{ frames, maxRaysPerFrame, maxDispatchesPerFrame, totalRays, rayBudget, oversizeChunks, fenceGiveUps }. - All vec3 helpers return new tables — none mutate their arguments. Safe to call in hot paths but allocates per call.
gatherMeshInstanceswalksentity.findAlland materialises each mesh-bearing entity's CPU item (ecs.Meshguid → mesh asset →ref:load()), so the engine-side BVH build can read the geometry — triangles never enter the Luau heap. Callrelease()after the build; assets whosekeepCpusetting holds them stay resident.surfaceAttributesdeduplicates the texture averaging through thecachetable — pass one table per gather so entities sharing a material average its texture once. Albedo lanes are clamped to 0.96 so multi-bounce transport converges.