GPU Lightmap Baking in Unity (Custom Baking Tools Guide)

How to Build a Hades‑Style Encounter Flow Generator in Unity
August 22, 2026
Real‑Time Volumetric Fog in Unity Using 3D Textures and Compute Shaders
August 24, 2026
How to Build a Hades‑Style Encounter Flow Generator in Unity
August 22, 2026
Real‑Time Volumetric Fog in Unity Using 3D Textures and Compute Shaders
August 24, 2026

GPU Lightmap Baking in Unity (Custom Baking Tools Guide)

Lightmap baking is one of the most expensive offline processes in Unity. Traditional CPU baking (Progressive CPU Lightmapper) is extremely slow for large scenes and high-resolution maps.
To overcome this, many studios build GPU-accelerated custom baking tools using compute shaders, RTX hardware, or hybrid CPU/GPU workloads.

This guide explores how GPU lightmap baking works, how to design a custom Unity-based GPU baker, how data flows from scene geometry to compute shaders, and how to generate a final lightmap texture.


1. What Is GPU Lightmap Baking?

GPU baking is a technique where the heavy calculations of lightmap generation (ray casting, irradiance gathering, global illumination, denoising) are performed on the GPU instead of the CPU.

A GPU baking system usually handles:

  • Ray generation on the GPU
  • Sampling direct and indirect lighting
  • Bouncing rays using BVH acceleration structures
  • Accumulating results into texels
  • Denoising using compute filters
  • Writing out final textures to disk

With massive parallelism, GPUs can deliver performance gains of:
10x–100x faster baking compared to CPU‑based lightmappers.


2. How Unity Lightmap Baking Internally Works

Unity’s lightmap generation process follows these general steps:

  1. Unwrap meshes for lightmapping (UV charting)
  2. Generate lightmap texels and world positions
  3. Cast rays (direct + indirect) per texel
  4. Accumulate lighting into the lightmap buffer
  5. Denoise + pack maps
  6. Save the final texture assets

The main expensive part is step 3 and 4, ray casting and lighting accumulation — which we will port to the GPU.


3. Designing a Custom GPU Lightmap Baking Architecture

A minimal GPU lightmap baker consists of these components:

  • Texel extractor – generates world-space positions for UV texels
  • Compute shader raycaster – shoots rays from texels
  • GPU buffers for geometry, materials, light data
  • Irradiance accumulator – gathers and averages samples
  • Denoiser (optional)
  • Texture writer – writes results into a Texture2D

Below is a simplified architecture diagram:

Unity Meshes → Texel Generator → Compute Shader (Ray Tracing) → Irradiance Buffer → Denoise → TextureWriter → Lightmap PNG

4. Step-by-Step Implementation in Unity

4.1 Extract Texels for Lightmap Baking

For each mesh with lightmap UVs, we must convert UV coordinates to world-space positions based on interpolated barycentric coordinates.

Sample C# extractor:

public struct TexelData
{
    public Vector3 worldPos;
    public Vector3 normal;
    public int lightmapIndex;
    public Vector2 uv;
}

public static List<TexelData> ExtractTexels(Mesh mesh, Transform t, int resolution)
{
    List<TexelData> texels = new();

    Vector2[] uvs = mesh.uv2;
    if (uvs == null || uvs.Length == 0) return texels;

    for (int i = 0; i < mesh.triangles.Length; i += 3)
    {
        int a = mesh.triangles[i];
        int b = mesh.triangles[i + 1];
        int c = mesh.triangles[i + 2];

        Vector2 uva = uvs[a];
        Vector2 uvb = uvs[b];
        Vector2 uvc = uvs[c];

        // Iterate texels inside triangle's UV space
        for (int x = 0; x < resolution; x++)
        for (int y = 0; y < resolution; y++)
        {
            Vector2 uv = new Vector2(x / (float)resolution, y / (float)resolution);
            if (!PointInTriangle(uv, uva, uvb, uvc)) continue;

            // Interpolate world position
            Vector3 pos = BarycentricInterpolate(mesh, t, a, b, c, uv, uva, uvb, uvc);

            texels.Add(new TexelData
            {
                worldPos = pos,
                normal = t.TransformDirection(mesh.normals[a]),
                uv = uv
            });
        }
    }

    return texels;
}

This builds the CPU-side texel list, which will be uploaded to the GPU.


4.2 Upload Geometry to GPU

We need mesh vertices, normals, indices, material IDs, and lights.
Store these in StructuredBuffers:

  • Vertex buffer
  • Triangle buffer
  • Material buffer
  • Light buffer
ComputeBuffer vertexBuffer;
ComputeBuffer triangleBuffer;
ComputeBuffer lightBuffer;

void UploadData()
{
    vertexBuffer = new ComputeBuffer(verts.Length, sizeof(float) * 3);
    vertexBuffer.SetData(verts);

    triangleBuffer = new ComputeBuffer(tris.Length, sizeof(int) * 3);
    triangleBuffer.SetData(tris);

    lightBuffer = new ComputeBuffer(lights.Length, sizeof(float) * 8);
    lightBuffer.SetData(lights);
}

5. Compute Shader for GPU Ray Casting

This is the core: each texel generates samples (rays) toward the scene.
We use Unity compute shaders (HLSL).

A minimal compute kernel:

// File: LightmapRaytrace.compute

#pragma kernel CSMain

StructuredBuffer&lt;float3&gt; TexelPositions;
StructuredBuffer&lt;Triangle&gt; Triangles;
RWStructuredBuffer&lt;float3&gt; Irradiance;

[numthreads(64,1,1)]
void CSMain(uint id : SV_DispatchThreadID)
{
    float3 pos = TexelPositions[id];

    float3 accumulated = 0;
    int sampleCount = 32; // example sample count

    for (int i = 0; i < sampleCount; i++)
    {
        float3 dir = RandomHemisphereDirection(normal);
        accumulated += TraceRay(pos, dir);
    }

    Irradiance[id] = accumulated / sampleCount;
}

In real GPU bakers, TraceRay() uses BVH acceleration instead of naive triangle iteration.


6. Integrating Compute Shader With C#

public ComputeShader raytraceShader;
ComputeBuffer texelBuffer;
ComputeBuffer irradianceBuffer;

public Texture2D BakeLightmap(List&lt;TexelData&gt; texels, int width, int height)
{
    texelBuffer = new ComputeBuffer(texels.Count, System.Runtime.InteropServices.Marshal.SizeOf(typeof(TexelData)));
    texelBuffer.SetData(texels);

    irradianceBuffer = new ComputeBuffer(texels.Count, sizeof(float) * 3);

    int kernel = raytraceShader.FindKernel("CSMain");

    raytraceShader.SetBuffer(kernel, "TexelPositions", texelBuffer);
    raytraceShader.SetBuffer(kernel, "Irradiance", irradianceBuffer);

    int groups = Mathf.CeilToInt(texels.Count / 64.0f);
    raytraceShader.Dispatch(kernel, groups, 1, 1);

    // Read results back
    Vector3[] result = new Vector3[texels.Count];
    irradianceBuffer.GetData(result);

    return PackToTexture(result, texels, width, height);
}

7. Packing Irradiance Into a Lightmap Texture

After computing lighting per texel, we convert data into a regular Texture2D lightmap.

Texture2D PackToTexture(Vector3[] samples, List&lt;TexelData&gt; texels, int width, int height)
{
    Texture2D lightmap = new Texture2D(width, height, TextureFormat.RGBAHalf, false);

    Color[] pixels = new Color[width * height];

    for (int i = 0; i < texels.Count; i++)
    {
        int x = (int)(texels[i].uv.x * width);
        int y = (int)(texels[i].uv.y * height);

        pixels[y * width + x] = new Color(
            samples[i].x,
            samples[i].y,
            samples[i].z,
            1.0f
        );
    }

    lightmap.SetPixels(pixels);
    lightmap.Apply();

    return lightmap;
}

8. Adding a GPU Denoiser

GPU bakers almost always need denoisers due to Monte Carlo sampling noise.
You can use:

  • Compute Shader Bilateral Filter
  • Wavelet Filter
  • SVGF (Spatiotemporal Variance-Guided Filtering)
  • Optix Denoiser (if using native plugin)

Example bilateral filter kernel:

// LightmapDenoise.compute

#pragma kernel Denoise

RWTexture2D&lt;float4&gt; Lightmap;

[numthreads(8,8,1)]
void Denoise(uint2 id : SV_DispatchThreadID)
{
    float3 sum = 0;
    float wsum = 0;

    for (int x = -2; x <= 2; x++)
    for (int y = -2; y <= 2; y++)
    {
        float3 col = Lightmap[id + uint2(x,y)].rgb;
        float w = 1; // Add bilateral weighting here
        sum += col * w;
        wsum += w;
    }

    Lightmap[id] = float4(sum / wsum, 1.0);
}

9. Exporting the Lightmap Texture to Disk

public static void SaveLightmap(Texture2D tex, string path)
{
    byte[] png = tex.EncodeToPNG();
    System.IO.File.WriteAllBytes(path, png);

#if UNITY_EDITOR
    UnityEditor.AssetDatabase.Refresh();
#endif
}

10. Optional: Hybrid RTX Ray Tracing via Native Plugin

For real Hades-, UE5-, or Unity Enemies-level baking speed, you can integrate:

  • NVIDIA OptiX
  • DirectX Raytracing (DXR)
  • Vulkan RT

This requires native plugins (C++ DLL), but the flow stays the same: Unity sends mesh + texel data → plugin → GPU returns irradiance values.


11. Summary

Custom GPU lightmap baking inside Unity is absolutely achievable and dramatically faster than CPU Progressive baking.
A GPU baker requires:

  • UV texel extraction
  • Geometry & lighting buffers
  • Compute shader raycaster
  • Irradiance accumulation
  • Denoising (optional)
  • Texture packing & exporting

This architecture gives you:

  • 10x–100x baking speed
  • Fully customizable GI behavior
  • Integration with procedural tools
  • The ability to run baking in‑game

With this foundation, you can extend it toward hybrid RTX baking, unfolded UV debugging tools, and even in-editor real-time GI previews.

Leave a Reply

Your email address will not be published. Required fields are marked *


Skip to toolbar