Unity5动态更新场景Lightmap,支持多张Lightmap混合

时间:2021-06-02 16:11:27

参考链接:http://www.xuanyusong.com/archives/3807
其它关于Unity5的光照系统神马的,网上很多资源,这几个文章良大本人觉得是很有价值的:

Unity 5 中的全局光照技术详解

http://www.gameres.com/forum.php?mod=viewthread&tid=446301

详解Unity 5 全局光照系统Enlighten问题

http://forum.china.unity3d.com/thread-14559-1-1.html
http://forum.china.unity3d.com/thread-14700-1-1.html

为什么要动态更新Lightmap

项目内需要对项目进行动态更新或者制作如白天黑夜等,同一场景不同光照渲染的情况。

将场景(.unity)文件转换成(.prefab)文件,能够更加*的操作场景资源的更换。

修改功能支持

  • 添加多张光照贴图烘焙

具体代码如下:


using UnityEngine;
using System.Collections.Generic;
using System.IO;
using GOE.Scene;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using SceneManager = UnityEngine.SceneManagement.SceneManager;

/// <summary>
/// 描述:动态烘焙场景光照模型,并产生对应的Prefab文件
/// <para>创建时间:2016-06-15</para>
/// </summary>
public sealed class LightMapEditor
{

    private const string LightMapsDir = "Resources/Lightmaps/";

    private static List<RemapTexture2D> sceneLightmaps = new List<RemapTexture2D>();

    public static void UpdateLightmaps()
    {
        PrefabLightmapData pld = GameObject.FindObjectOfType<PrefabLightmapData>();
        if (pld == null) return;

        LightmapSettings.lightmaps = null;
        PrefabLightmapData.ApplyLightmaps(pld.mRendererInfos , pld.mLightmapFars , pld.mLightmapNears);

        Debug.Log("Prefab Lightmap updated");
    }

    public static void GenLightmap()
    {
        genBakeLightmapAndPrefab(false);

        EditorSceneManager.SaveOpenScenes();
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();

        Debug.Log("----------------Update to Prefab Lightmap Finish -------------------------");
    }

    /// <summary>
    /// 生成lightmap和prefab资源
    /// </summary>
    private static void genBakeLightmapAndPrefab()
    {
        if (Lightmapping.giWorkflowMode != Lightmapping.GIWorkflowMode.OnDemand)
        {
            Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
            return;
        }
        Debug.ClearDeveloperConsole();

        PrefabLightmapData[] pldArr = GameObject.FindObjectsOfType<PrefabLightmapData>();
        if (pldArr == null || pldArr.Length <= 0)
        {
            EditorUtility.DisplayDialog("提示", "没有找到必要的脚本PrefabLightmapData,请检查场景", "OK");
            return;
        }

        Lightmapping.Bake();
        sceneLightmaps.Clear();

        string path = Path.Combine(Application.dataPath, LightMapsDir);
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }

        Scene curScene = EditorSceneManager.GetActiveScene();
        string sceneName = Path.GetFileNameWithoutExtension(curScene.name);
        string scenePath = Path.GetDirectoryName(curScene.path) + "/" + sceneName + "/";
        string resourcesPath = Path.GetDirectoryName(curScene.path) + "/" + sceneName + "_lightmap/" + sceneName;

        foreach (PrefabLightmapData pld in pldArr)
        {
            GameObject gObj = pld.gameObject;
            List<RendererInfo> renderers = new List<RendererInfo>();
            List<Texture2D> lightmapFars = new List<Texture2D>();
            List<Texture2D> lightmapNears = new List<Texture2D>();

            genLightmapInfo(scenePath, resourcesPath, gObj, renderers, lightmapFars, lightmapNears);

            pld.mRendererInfos = renderers.ToArray();
            pld.mLightmapFars = lightmapFars.ToArray();
            pld.mLightmapNears = lightmapNears.ToArray();

            GameObject targetPrefab = PrefabUtility.GetPrefabParent(gObj) as GameObject;           

            if (targetPrefab != null)
            {
                //自定义存放的路径
                PrefabUtility.ReplacePrefab(gObj, targetPrefab);
            }
            else
            {
                //默认路径
                string prefabPath = Path.GetDirectoryName(curScene.path) + "/" + sceneName + ".prefab";
                PrefabUtility.CreatePrefab(prefabPath, gObj, ReplacePrefabOptions.ConnectToPrefab);
            }

            //改变当前场景中的光照贴图信息
            PrefabLightmapData.ApplyLightmaps(pld.mRendererInfos, pld.mLightmapFars, pld.mLightmapNears);
        }
    }




    private static void genLightmapInfo(string scenePath , string resourcePath , GameObject root, 
                                        List<RendererInfo> renderers, List<Texture2D> lightmapFars,
                                        List<Texture2D> lightmapNears)
    {
        MeshRenderer[] subRenderers = root.GetComponentsInChildren<MeshRenderer>();

        LightmapData[] srcLightData = LightmapSettings.lightmaps;

        foreach (MeshRenderer meshRenderer in subRenderers)
        {
            if(meshRenderer.lightmapIndex == -1)    continue;

            RendererInfo renderInfo = new RendererInfo();
            renderInfo.renderer = meshRenderer;
            renderInfo.LightmapIndex = meshRenderer.lightmapIndex;
            renderInfo.LightmapOffsetScale = meshRenderer.lightmapScaleOffset;

            Texture2D lightmapFar = srcLightData[meshRenderer.lightmapIndex].lightmapFar;
            Texture2D lightmapNear = srcLightData[meshRenderer.lightmapIndex].lightmapNear;

            int sceneCacheIndex = addLightmap(scenePath, resourcePath, renderInfo.LightmapIndex, lightmapFar,
                lightmapNear);

            renderInfo.LightmapIndex = lightmapFars.IndexOf(sceneLightmaps[sceneCacheIndex].LightmapFar);
            if (renderInfo.LightmapIndex == -1)
            {
                renderInfo.LightmapIndex = lightmapFars.Count;
                lightmapFars.Add(sceneLightmaps[sceneCacheIndex].LightmapFar);
                lightmapNears.Add(sceneLightmaps[sceneCacheIndex].LightmapNear);
            }

            renderers.Add(renderInfo);
        }
    }


    private static int addLightmap(string scenePath, string resourcePath, int originalLightmapIndex,
        Texture2D lightmapFar,
        Texture2D lightmapNear)
    {

        for (int i = 0; i < sceneLightmaps.Count; i++)
        {
            if (sceneLightmaps[i].OriginalLightmapIndex == originalLightmapIndex)
            {
                return i;
            }
        }


        RemapTexture2D remapTex = new RemapTexture2D();
        remapTex.OriginalLightmapIndex = originalLightmapIndex;
        remapTex.OrginalLightmap = lightmapFar;

        string fileName = scenePath + "Lightmap-" + originalLightmapIndex;
        remapTex.LightmapFar = getLightmapAsset(fileName + "_comp_light.exr", resourcePath + "_light",
            originalLightmapIndex);

        if(lightmapNear != null)
            remapTex.LightmapNear = getLightmapAsset(fileName + "_comp_dir.exr", resourcePath + "_dir",
                originalLightmapIndex);

        sceneLightmaps.Add(remapTex);

        return sceneLightmaps.Count - 1;
    }


    private static Texture2D getLightmapAsset(string fileName, string resourecPath, int originalLightmapIndex)
    {
        TextureImporter importer = AssetImporter.GetAtPath(fileName) as TextureImporter;
        if (importer == null)   return null;
        importer.isReadable = true;
        AssetDatabase.ImportAsset(fileName , ImportAssetOptions.ForceUpdate);

        Texture2D assetLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(fileName);
        string assetPath = resourecPath + "_" + originalLightmapIndex + ".asset";
        Texture2D newLightmap = GameObject.Instantiate<Texture2D>(assetLightmap);

        string dir = Path.GetDirectoryName(assetPath);
        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

// byte[] bytes = newLightmap.EncodeToPNG();
// File.WriteAllBytes(assetPath, bytes);
        AssetDatabase.CreateAsset(newLightmap , assetPath);


        newLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);

        importer.isReadable = false;
        AssetDatabase.ImportAsset(assetPath , ImportAssetOptions.ForceUpdate);
        return newLightmap;
    }
}

PrefabLightData

用于还原和记录光照数据


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace GOE.Scene
{
    /// <summary>
    /// 描述:光照模型预设
    /// </summary>
    [DisallowMultipleComponent , ExecuteInEditMode]
    public class PrefabLightmapData : MonoBehaviour
    {
        [SerializeField]
        public RendererInfo[] mRendererInfos;

        [SerializeField]
        public Texture2D[] mLightmapFars;

        [SerializeField]
        public Texture2D[] mLightmapNears;

        void Awake()
        {
            ApplyLightmaps(mRendererInfos , mLightmapFars , mLightmapNears);

            //设置原来烘焙时的光照模式,这个不设置正确,默认模式就会只显示光照贴图
            LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        }

        void Start()
        {
// StaticBatchingUtility.Combine(this.gameObject);
        }

        /// <summary>
        /// 应用光照模型,根据指定的渲染信息
        /// </summary>
        /// <param name="rendererInfos"></param>
        /// <param name="lightmapFars"></param>
        /// <param name="lightmapNears"></param>
        public static void ApplyLightmaps(RendererInfo[] rendererInfos, Texture2D[] lightmapFars,
                                          Texture2D[] lightmapNears)
        {
            if (rendererInfos == null || rendererInfos.Length <= 0)
            {
                Debug.LogWarning("<<PrefabLightmapData , ApplyLightmaps>> renderer info is null !");
                return;
            }

            LightmapData[] settingLightMaps = LightmapSettings.lightmaps;
            int[] lightmapOffsetIndex = new int[lightmapFars.Length];
            List<LightmapData> combinedLightmaps = new List<LightmapData>();

            bool existAlready = false;
            for (int i = 0; i < lightmapFars.Length; i++)
            {
                existAlready = false;
                for (int j = 0; j < settingLightMaps.Length; j++)
                {
                    if (lightmapFars[i] == settingLightMaps[j].lightmapFar)
                    {
                        lightmapOffsetIndex[i] = j;
                        existAlready = true;
                        break;
                    }
                }

                //如果不存在,则创建新的光照数据
                if (!existAlready)
                {
                    lightmapOffsetIndex[i] = settingLightMaps.Length + combinedLightmaps.Count;

                    LightmapData newLightData = new LightmapData();
                    newLightData.lightmapFar = lightmapFars[i];
                    newLightData.lightmapNear = lightmapNears[i];
                    combinedLightmaps.Add(newLightData);
                }
            }

            //组合数据
            LightmapData[] finalCombinedLightData = new LightmapData[combinedLightmaps.Count + settingLightMaps.Length];
            settingLightMaps.CopyTo(finalCombinedLightData , 0);
            combinedLightmaps.CopyTo(finalCombinedLightData , settingLightMaps.Length);
            combinedLightmaps.Clear();

            applyRendererInfo(rendererInfos , lightmapOffsetIndex);

            //重新绑定
            LightmapSettings.lightmaps = finalCombinedLightData;

        }


        /// <summary>
        /// 应用渲染信息
        /// </summary>
        /// <param name="rendererInfos"></param>
        /// <param name="offsetIndexs"></param>
        private static void applyRendererInfo(RendererInfo[] rendererInfos, int[] offsetIndexs)
        {
            for (int i = 0; i < rendererInfos.Length; i++)
            {
                RendererInfo info = rendererInfos[i];

                info.renderer.lightmapIndex = offsetIndexs[info.LightmapIndex];
                info.renderer.lightmapScaleOffset = info.LightmapOffsetScale;

            }
        }
    }

}

其它脚本


using UnityEngine;

namespace GOE.Scene
{
    /// <summary>
    /// 渲染信息
    /// </summary>
    [System.Serializable]
    public struct RendererInfo
    {
        public Renderer renderer;
        public int LightmapIndex;
        public Vector4 LightmapOffsetScale;    //偏移和缩放
    }

    /// <summary>
    /// 重渲贴图
    /// </summary>
    [System.Serializable]
    public struct RemapTexture2D
    {
        public int OriginalLightmapIndex;
        public Texture2D OrginalLightmap;

        public Texture2D LightmapFar;
        public Texture2D LightmapNear;
    }
}