#UnityTips – Unity 5 Lightmapping Tips

Unity 5’s new lightmapping system can look gorgeous but still has a way to go before the process is as smooth as beast was (which is saying something), so I’ve gathered up all the bits and pieces I’ve stumbled on this last week to hopefully fix some woes.

Map your own lightmap UVs 

Unitys unwrapper makes a good effort but can’t compete with a 3D artist who understands the topology of the model. Try to hide seams in by having them in the edges and creases of the model, and where possible try to have the edges of the UV shells be straight vertically or horizontally, they’ll use the UV space better and will reduce the appearance of aliasing (this applies especially to cylinders like pipes), UV distortion barely shows up in lightmaps so favor distortion over aliasing. Unity 4.x used to give you a warning if your lightmap UVs extended outside the 0 – 1 UV range but no longer does in 5 so watch out for this as Unity assumes 0 – 1 when packing and any overlapping UVs will cause speckling. To help with this I wrote a function in an Editor script that goes through each mesh filter in the scene that is lightmap static and checks it’s shared mesh’s lightmap UVs are in range, logging the ones that aren’t. 

public static void CheckGIUVs(){
    //get objects marked as lightmap static
    List<Renderer> renderers = new List<Renderer>( GameObject.FindObjectsOfType (typeof(Renderer)) as Renderer[]);
    renderers.RemoveAll ((obj) => !GameObjectUtility.AreStaticEditorFlagsSet (obj.gameObject, StaticEditorFlags.LightmapStatic));

    //remove lightmap scale 0 stuff
    renderers.RemoveAll ((obj) => new SerializedObject (obj).FindProperty ("m_ScaleInLightmap").floatValue == 0);

    //get the meshes
    List<Renderer> badRenderers = new List<Renderer> ();

    for (int i = 0; i < renderers.Count; i++){
        MeshFilter mf = renderers[i].GetComponent<MeshFilter>();
        if (mf){
            if (mf.sharedMesh.uv2 == null){
                for (int j = 0; j < mf.sharedMesh.uv2.Length; j++){
                    Vector2 uv = mf.sharedMesh.uv2[j];
                    if (uv.x > 1f || uv.x < 0f ||
                        uv.y > 1f || uv.y < 0f){

    string message = "GIUVs Out Of Range:";
    badRenderers.ForEach ((obj) => message += " " + obj.name + ",");

Scale in Lightmap 0

Some objects don’t lend themselves to lightmapping because they are too fiddly, if they were lightmapped they would end up covered in obvious seams while using a large area of the lightmap. In Unity 4 I commonly set these objects scale in lightmap to zero to have them cast shadows in the lightmap but not use the lightmap themselves (they would use light probes or realtime lights). Unity 5 still supports this but there are some extra problems that are not obvious and can cause problems. The biggest problem is that these scale zero objects still need lightmap UVs! They don’t need to be good but they need to be there, scale zero objects without lightmap UVs will cause speckling all over lightmap 0. When I encountered this I wrote another script to find all the scale zero objects in the scene and give them a second UV set if needed, that just made every uv2 (0.5,0.5). The other problem is scale zero objects still cast realtime shadows onto lightmapped objects, though I don’t have a solution for this yet.

public static void CreateGIUVsForSelected(){
		List<Mesh> sharedMeshes = new List<Mesh> ();

		for(int i = 0; i < Selection.gameObjects.Length; i++){
			MeshFilter mf = Selection.gameObjects[i].GetComponent<MeshFilter>();
			if (mf && mf.sharedMesh != null && !sharedMeshes.Contains(mf.sharedMesh)){

		for (int i = 0; i < sharedMeshes.Count; i++){
			Vector2[] uv2s = new Vector2[sharedMeshes[i].vertexCount];
			for (int j = 0; j < uv2s.Length; j++){
				uv2s[j] = Vector2.one * 0.5f;
			sharedMeshes[i].uv2 = uv2s;

Light Probes are Too Bright

This is less apparent in linear lighting mode but shows up very clearly in gamma, when comparing an object lit using light probes are far brighter than lightmapped and realtime lit objects. There isn’t exactly an easy solution to this one either and hopefully it will be fixed very soon, to work around this I ended up using a modified UnityCG.cginc where the output of ShadeSH9 is multiplied by 0.33 (there’s no logical reason behind 0.33 I just thought it looked right), this was included in the same folder as the rest of the shaders so they would pick it up.

EDIT: Vasilii (@Vas_Shu) spotted that saturate(ShadeSH9) works and is much more accurate than *0.33 as probes are now in HDR

After checking all the points on this list I was finally able to lightmap the scene to a quality that matched and improved on beast but it’s certainly not an easy process, hopefully this workflow will improve over the 5.x cycle.

Leave a comment

Your email address will not be published.