在很多情况下,我们为了提高渲染效率,一般都会让美术同学在制作场景时,设置场景相关节点的lightmap static属性,提前给整个场景烘培出静态的光照贴图lightmap,烘培的数据保存在场景目录下的LightmapSnapshot文件中,主要包括的数据有:

lightmaps:烘培出的光照贴图数组;

gameobject uid:被烘培的gameobject的唯一标识;

renderer的lightmapIndex:被烘培的gameobject的renderer组件所指向的光照贴图数组的索引;

renderer的lightmapScaleOffset:被烘培的gameobject的renderer组件所指向的光照贴图用于采样的区域坐标和宽高;

这个文件目前没有相关api读写,如果你想烘培完场景之后,把场景里面的gameobjet抽出来做成prefab,等切换完场景之后再用于动态加载是不可行的,因为抽出来的prefab在Instantiate之后将会是一个新的gameobject,uid自然和LightmapSnapshot文件里面记录的不一样,导致找不到对应的光照数据而造成模型没光照变暗或渲染错乱。

还有一种比较常见的需求是,在游戏运行时,通过更换光照贴图数据,营造场景在不同时间或季节的光照氛围,例如白天和黑夜等。

so,就算场景烘培完之后,我们还是要“动”它。

做法大概是,既然LightmapSnapshot文件我们不能动,那就把上面提到的光照数据保存到我们可以控制的文件里面,例如prefab。

首先,给场景根节点挂一个自定义组件,用于保存烘培出的光照贴图数组和烘培模式:


using          UnityEngine         ;


using          System         .         Collections         ;


                  


[         ExecuteInEditMode         ]


public                   class                   SceneLightMapSetting                   :                   MonoBehaviour                   {


             public                   Texture2D                   [         ]         lightmapFar         ,                   lightmapNear         ;


             public                   LightmapsMode          mode         ;


                  


             public                   void                   SaveSettings         (         )


             {


                 mode                   =                   LightmapSettings         .         lightmapsMode         ;


                 lightmapFar                   =                   null         ;


                 lightmapNear                   =                   null         ;


                 if                   (         LightmapSettings         .         lightmaps                   !=                   null                   &&                   LightmapSettings         .         lightmaps         .         Length                   >                   0         )


                 {


                     int                   l                   =                   LightmapSettings         .         lightmaps         .         Length         ;


                     lightmapFar                   =                   new                   Texture2D         [         l         ]         ;


                     lightmapNear                   =                   new                   Texture2D         [         l         ]         ;


                     for                   (         int                   i                   =                   0         ;                   i                   <                   l         ;                   i         ++         )


                     {


                         lightmapFar         [         i         ]                   =                   LightmapSettings         .         lightmaps         [         i         ]         .         lightmapFar         ;


                         lightmapNear         [         i         ]                   =                   LightmapSettings         .         lightmaps         [         i         ]         .         lightmapNear         ;


                     }


                 }


                 RendererLightMapSetting         [         ]                   savers                   =                   Transform         .         FindObjectsOfType         <         RendererLightMapSetting         >         (         )         ;


                 foreach         (         RendererLightMapSetting                   s                   in                   savers         )


                 {


                     s         .         SaveSettings         (         )         ;


                 }


            }


                  


             public                   void                   LoadSettings         (         )


             {


                 LightmapSettings         .         lightmapsMode                   =                   mode         ;


                 int                   l1                   =                   (         lightmapFar                   ==                   null         )                   ?                   0                   :                   lightmapFar         .         Length         ;


                 int                   l2                   =                   (         lightmapNear                   ==                   null         )                   ?                   0                   :                   lightmapNear         .         Length         ;


                 int                   l                   =                   (         l1                   <                   l2         )                   ?                   l2                   :                   l1         ;


                 LightmapData         [         ]                   lightmaps                   =                   null         ;


                 if                   (         l                   >                   0         )


                 {


                     lightmaps                   =                   new                   LightmapData         [         l         ]         ;


                     for                   (         int                   i                   =                   0         ;                   i                   <                   l         ;                   i         ++         )


                     {


                         lightmaps         [         i         ]                   =                   new                   LightmapData         (         )         ;


                         if                   (         i                   <                   l1         )


                             lightmaps         [         i         ]         .         lightmapFar                   =                   lightmapFar         [         i         ]         ;


                         if                   (         i                   <                   l2         )


                             lightmaps         [         i         ]         .         lightmapNear                   =                   lightmapNear         [         i         ]         ;


                     }


                  


                     LightmapSettings         .         lightmaps                   =                   lightmaps         ;


                 }


             }





             void                   OnEnable         (         )


             {


#if UNITY_EDITOR


                 UnityEditor         .         Lightmapping         .         completed                   +=                   SaveSettings         ;


#endif


             }


             void                   OnDisable         (         )


             {


#if UNITY_EDITOR


                 UnityEditor         .         Lightmapping         .         completed                   -=                   SaveSettings         ;


#endif


             }


                  


             void                   Awake                   (         )                   {


                 if         (         Application         .         isPlaying         )         {


                     LoadSettings         (         )         ;


                 }


             }


}


再给场景里面被烘培的gameobject挂一组件,用于保存光照贴图数组的索引和光照贴图的区域坐标和宽高数据:

using          UnityEngine         ;


using          System         .         Collections         ;


                  


[         ExecuteInEditMode         ]


public                   class                   RendererLightMapSetting                   :                   MonoBehaviour                   {


             public                   int                   lightmapIndex         ;


             public                   Vector4          lightmapScaleOffset         ;


                  


             public                   void                   SaveSettings         (         )


             {


                 if         (         !         IsLightMapGo         (         gameObject         )         )         {


                     return         ;


                 }


                 Renderer          renderer                   =                   GetComponent         <         Renderer         >         (         )         ;


                 lightmapIndex                   =                   renderer         .         lightmapIndex         ;


                 lightmapScaleOffset                   =                   renderer         .         lightmapScaleOffset         ;


             }


                  


             public                   void                   LoadSettings         (         )


             {


                 if         (         !         IsLightMapGo         (         gameObject         )         )         {


                     return         ;


                 }


                  


                 Renderer          renderer                   =                   GetComponent         <         Renderer         >         (         )         ;


                 renderer         .         lightmapIndex                   =                   lightmapIndex         ;


                 renderer         .         lightmapScaleOffset                   =                   lightmapScaleOffset         ;


             }


                  


             public                   static                   bool                   IsLightMapGo         (         GameObject          go         )         {


                 if         (         go                   ==                   null         )         {


                     return                   false         ;


                 }


                 Renderer          renderer                   =                   go         .         GetComponent         <         Renderer         >         (         )         ;


                 if         (         renderer                   ==                   null         )         {


                     return                   false         ;


                 }


                 return                   true         ;


             }


                  


             void                   Awake                   (         )                   {


                 if                   (         Application         .         isPlaying         )                   {


                     LoadSettings                   (         )         ;


                 }


             }


}




如果手动挂上面两个脚本的话,繁琐且容易出错,so,写工具接口,用工具扫场景挂脚本:


using          UnityEngine         ;


using          UnityEditor         ;


using          System         .         IO         ;


using          System         .         Text         ;


using          System         .         Linq         ;


using          System         .         Collections         ;


using          System         .         Collections         .         Generic         ;


using          System         ;


                  


public                   class                   SceneTools


{


             static                   string         [         ]                   _sNeedAssetType                   =                   new                   string         [         1         ]         {


                 ".unity"         ,


             }         ;


                  


             [         MenuItem         (         "场景相关/1. 保存场景光照贴图信息"         ,                   false         ,                   111         )         ]


             public                   static                   void                   SaveSceneMapLightSetting         (         )


             {


                 UnityEngine         .         Object         [         ]                   selObjs                   =                   Selection         .         GetFiltered                   (         typeof         (         UnityEngine         .         Object         )         ,                   SelectionMode         .         DeepAssets         )         ;


                 if                   (         selObjs                   ==                   null                   ||                   selObjs         .         Length                   ==                   0         )                   {


                     Debug         .         LogError                   (         "请到\"Scenes\" 目录下选中需要保存场景光照贴图信息的场景!"         )         ;


                     return         ;


                 }





                 string                   assetPath         ;


                 UnityEngine         .         Object                   assetObj                   =                   null         ;


                 GameObject          gameObj                   =                   null         ;


                 SceneLightMapSetting          slms                   =                   null         ;


                 RendererLightMapSetting          rlms                   =                   null         ;


                 bool                   needSave                   =                   false         ;


                 for                   (         int                   i                   =                   0         ;                   i                   <                   selObjs         .         Length         ;                   ++         i         )                   {


                     needSave                   =                   false         ;


                     assetPath                   =                   AssetDatabase         .         GetAssetPath                   (         selObjs                   [         i         ]         )         ;


                     foreach                   (         string                   extName          in                   _sNeedAssetType         )                   {


                         if                   (         assetPath         .         EndsWith                   (         extName         )         )                   {


                             needSave                   =                   true         ;


                             break         ;


                         }


                     }


                     if                   (         !         needSave         )


                         continue         ;





                     assetObj                   =                   AssetDatabase         .         LoadAssetAtPath                   (         assetPath         ,                   typeof         (         UnityEngine         .         Object         )         )                   as                   UnityEngine         .         Object         ;


                     EditorApplication         .         OpenScene                   (         assetPath         )         ;





                     gameObj                   =                   GameObject         .         Find                   (         "scene_root"         )         ;


                     if                   (         gameObj                   ==                   null         )                   {


                         Debug         .         LogError                   (         "不合法的场景:场景没有scene_root根节点!!"         )         ;


                         continue         ;


                     }


                  


                     slms                   =                   gameObj         .         GetComponent         <         SceneLightMapSetting         >         (         )         ;


                     if         (         slms                   ==                   null         )         {


                         slms                   =                   gameObj         .         AddComponent         <         SceneLightMapSetting         >         (         )         ;


                     }


                  


                     Renderer         [         ]                   savers                   =                   Transform         .         FindObjectsOfType         <         Renderer         >         (         )         ;


                     foreach         (         Renderer                   s                   in                   savers         )


                     {


                         if         (         s         .         lightmapIndex                   !=                   -         1         )         {


                             rlms                   =                   s         .         gameObject         .         GetComponent         <         RendererLightMapSetting         >         (         )         ;


                             if         (         rlms                   ==                   null         )         {


                                 rlms                   =                   s         .         gameObject         .         AddComponent         <         RendererLightMapSetting         >         (         )         ;


                             }


                         }


                     }


                  


                     slms         .         SaveSettings         (         )         ;


                  


                     EditorApplication         .         SaveScene         (         )         ;


                  


                     Debug         .         Log         (         string         .         Format         (         "场景{0}的光照贴图信息保存完成"         ,                   assetObj         .         name         )         )         ;


                 }


                 EditorApplication         .         SaveAssets         (         )         ;


             }


}


没截图从觉得少点什么,来,上图,先打开一个被烘培过的场景,场景根节点为scene_root,被烘培的gameobjet是地板、一颗小草、带logo的立方体:

unity贴图压缩 unity 贴图设置_LightingMap

然后使用工具给场景里面的相关节点挂脚本存数据:

unity贴图压缩 unity 贴图设置_Unity3d_02

把场景根节点scene_root拖出来做成prefab,然后删掉场景里面的scene_root,再把scene_root.prefab拖进场景里面:

unity贴图压缩 unity 贴图设置_Unity3d_03

此时模型是丢失了光照数据的,原因截图已说明。接着运行场景:

unity贴图压缩 unity 贴图设置_LightingMap_04

模型的光照又回来啦,原因截图已说明。