Search Unity

How to have Lightmapping WITHOUT breaking dynamic batching?

Discussion in 'Editor & General Support' started by Trithilon, Aug 26, 2013.

  1. Trithilon

    Trithilon

    Joined:
    Aug 2, 2012
    Posts:
    30
    Hello,

    I am working on a procedurally generated game where I would love to bake Lightmaps into my Prefab Chunks (which I assemble during runtime to create a level).

    I need to know the best way to do so without skyrocketing my drawcalls.

    I am only using Unlit Shaders.


    Thanks.
     
  2. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    3,276
    Are you placing the object procedurally or generating the whole mesh procedurally ?
    If you are targeting a minecraft style game you are probably not going to bake the light and batching is best on building a chunk mesh.
    The problem with light baking is that it adds hidden parameter to the material and thats braking the batching.
     
  3. Trithilon

    Trithilon

    Joined:
    Aug 2, 2012
    Posts:
    30
    No. We are just "placing" chunks procedurally. I am not really making meshes on the fly.

    So... before I build the game, I can place all the chunks in a level and build lightmaps for them via a directional light.

    Apparently, this plugin has a way of "repairing" the lightmaps to give you 1 draw call for your lightmapped meshes (but they need to use an NGUI atlas) :
    http://tracki.pl/atlas3d/
     
  4. Trithilon

    Trithilon

    Joined:
    Aug 2, 2012
    Posts:
    30
    *bump*
     
  5. BIG-BUG

    BIG-BUG

    Joined:
    Mar 29, 2009
    Posts:
    457
    *Doing*
    I've created this editor script just for fun:

    C#: "UpdateLightmapUV"
    Code (csharp):
    1.  
    2. /*Update Lightmap UV by Beast Setting
    3.  Use at your own risk.
    4.  This script will modify UV2 coordinates of all selected
    5.  mesh assets in the scene view.
    6. Warning:
    7. -The asset itself will be modified, which is not possible to Undo
    8. -Only execute once per mesh!
    9.  
    10. Usage:
    11. 1. Make sure all meshes have a UV2 set applied. The easy way is to just activate
    12.    "Generate Lightmap UVs" in the model import settings.
    13. 2. Mark objects as static and bake lightmap
    14. 3. Mark all static objects in scene view and call this script vie menu: Tools/Lightmap/Transfer Lightmap UV to Mesh
    15. 4. The lightmap atlas is now baked into the mesh UV2
    16.  
    17. (c) by Robert Hierl www.mein-murks.de */
    18.  
    19. using UnityEditor;
    20. using UnityEngine;
    21.  
    22. public class UpdateLightmapUV : MonoBehaviour {
    23.    
    24.     // Add a menu item
    25.     [MenuItem ("Tools/Lightmap/Transfer Lightmap UV to Mesh")]
    26.     static void TransferLightmapUVtoMesh ()
    27.     {
    28.         UpdateLightmapUVinSelection( );
    29.     }
    30.  
    31.     // Active only if mesh is selected in scene view - maybe bad for editor performance?!
    32.     [MenuItem ("Tools/Lightmap/Transfer Lightmap UV to Mesh", true)]
    33.     static bool ValidateTransferLightmapUVtoMesh () {
    34.        
    35.         return (CheckLightmapUVinSelection());
    36.     }
    37.        
    38.    
    39.     static bool CheckLightmapUVinSelection() {
    40.         return UpdateLightmapUVinSelection( true ) > 0 ? true: false;
    41.     }
    42.    
    43.    
    44.     static int UpdateLightmapUVinSelection( bool checkOnly = false ) {
    45.    
    46.         int meshesToUpdate = 0;
    47.        
    48.         foreach (Transform go in Selection.GetTransforms(SelectionMode.Unfiltered)) {
    49.        
    50.             MeshFilter meshFilter = go.GetComponent<MeshFilter>();
    51.            
    52.             if (meshFilter == null) continue;
    53.            
    54.             meshesToUpdate++;
    55.            
    56.             if (!checkOnly) UpdateMeshUV2( meshFilter.sharedMesh, go.renderer.lightmapTilingOffset );
    57.            
    58.         }
    59.        
    60.         if (!checkOnly) {
    61.             if (meshesToUpdate > 0)
    62.                 Debug.Log(meshesToUpdate + " meshes have been updated.");
    63.             else
    64.                 Debug.LogError( "No mesh to update" );
    65.         }
    66.            
    67.         return meshesToUpdate;
    68.        
    69.     }
    70.    
    71.     static void UpdateMeshUV2( Mesh mesh, Vector4 tilOff ) {
    72.            
    73.         //generate new lightmap UV first - does not help workflow here
    74.         //Unwrapping.GenerateSecondaryUVSet( mesh );
    75.        
    76.         //modify lightmap UV with tiling / offset from atlas setting
    77.         Vector2[] lmuv = mesh.uv2;
    78.  
    79.         for (int i = 0; i < mesh.uv2.Length; i++) {
    80.            
    81.             lmuv[i] = new Vector2(mesh.uv2[i].x * tilOff.x + tilOff.z, mesh.uv2[i].y * tilOff.y + tilOff.w);
    82.  
    83.         }
    84.         mesh.uv2 = lmuv;
    also you need this shader to show the lightmap (assign matching lightmap to second slot):

    Code (csharp):
    1. Shader "Custom/CustomLightmap" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _Lightmap ("Lightmap", 2D) = "white" {}
    5.     }
    6. SubShader {
    7.     Pass {
    8.        
    9.         CGPROGRAM
    10.         #pragma vertex vert
    11.         #pragma fragment frag
    12.         #include "UnityCG.cginc"
    13.        
    14.         sampler2D _MainTex;
    15.         float4 _MainTex_ST; //scale  position of _MainTex
    16.        
    17.         sampler2D _Lightmap;//lightmap
    18.         float4 _Lightmap_ST; //scale  position of lightmap
    19.        
    20.        
    21.         // vertex input: position, UV0, UV1
    22.         struct appdata {
    23.             float4 vertex   : POSITION;
    24.             float2 texcoord : TEXCOORD0;
    25.             float2 texcoord1: TEXCOORD1;
    26.         };
    27.        
    28.         struct v2f {
    29.             float4 pos  : SV_POSITION;
    30.             float2 txuv : TEXCOORD0;
    31.             float2 lmuv : TEXCOORD1;
    32.         };
    33.        
    34.         v2f vert (appdata v) {
    35.             v2f o;
    36.             o.pos   = mul( UNITY_MATRIX_MVP, v.vertex );
    37.             o.txuv  = TRANSFORM_TEX(v.texcoord.xy,_MainTex);
    38.             o.lmuv  = TRANSFORM_TEX(v.texcoord1.xy,_Lightmap);
    39.             return o;
    40.         }
    41.        
    42.         half4 frag( v2f i ) : COLOR {
    43.             half4 col   = tex2D(_MainTex, i.txuv.xy);
    44.             half4 lm    = tex2D(_Lightmap, i.lmuv.xy);
    45.             col.rgb     = col.rgb * DecodeLightmap(lm);
    46.             return col;
    47.         }
    48.         ENDCG
    49.         }
    50.     }
    51. }
    After baking the atlas the static meshes can be removed and replaced with dynamic ones. Those will batch if all the other requirements for dynamic batching are met.

    Good Luck!
     
    Last edited: Sep 4, 2013
  6. Trithilon

    Trithilon

    Joined:
    Aug 2, 2012
    Posts:
    30
    This is brilliant!
    Let me try this out and get back to you with the results. And since I am using only one level - it goes very well with this approach.
    I am guessing this should work with the stock Mobile/Unlit (Lightmapping supported) shader.

    Thanks! :D
     
  7. ErwanB

    ErwanB

    Joined:
    Nov 14, 2012
    Posts:
    25
    Nice!

    It works fine except that when I close Unity Editor, all changes to the light map UV are lost :eek:
    Right now I am using an editor script to re-apply the changes when the Editor starts but I a looking for a prettier solution.

    Is there a way to really persist the changes directly into the mesh or at least in Unity Library ?
    Another option would be to create new assets by cloning and applying the light map UV changes but that would be time consuming and not so elegant.

    Anyway, for anyone interested in this topic, BIG BUG's solution does work with standard Unity Shaders, as long as you reset the Atlas information of the mesh (tiling = 1 & offset = 0). But doing so you lose the Atlas information and won't be able to use it to re-apply the UV changes after restarting the Editor...
    So you'd better stick with the custom shader solution ;)
     
  8. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    655
    @Erwan Bézier Am investigating creating an asset to persist the changes here. Thoughts?
     
  9. Culzean

    Culzean

    Joined:
    Jan 25, 2014
    Posts:
    48
    I have found the StaticBatchUtility.Combine() method to work well in this case. I'm applying this after the values for lightmapOffset and lightmapIndex have been applied to the static renderMeshes. The lighting is calculated and the draw calls are back down to a nice low number.

    public classBatcher : MonoBehaviour {

    List<GameObject> children = newList<GameObject>();

    //Usethisforinitialization
    voidStart () {

    Init ();
    }

    public void Init() {

    foreach(Transformchildintransform) {
    children.Add(child.gameObject);
    }

    StaticBatchingUtility.Combine( children.ToArray(), gameObject );
    }
    }