首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之Unity5.x运行时动态更新烘培贴图(八十七)
2015
12-23

Unity3D研究院之Unity5.x运行时动态更新烘培贴图(八十七)

前段时间我研究过这个问题,但是没有解决只好作罢。今天刚好有人又问我这个问题,我得空查了一下还是找到了解决办法。另外也感谢问我的人,解答问题的同时也是我学习的过程。

运行时更新烘培贴图分两种情况

1、场景的物件没有发生变化(也就是说没有运行时加载在场景上的Prefab)此时可以直接更换烘培贴图。

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

	//烘培烘培贴图1
	public Texture2D greenLightMap;
	//烘培贴图2
	public Texture2D redLightMap;
	void OnGUI()
	{
		if(GUILayout.Button("green"))
		{
			LightmapData data = new LightmapData();
			data.lightmapFar = greenLightMap;
			LightmapSettings.lightmaps = new LightmapData[1]{data};
		}

		if(GUILayout.Button("red"))
		{
			LightmapData data = new LightmapData();
			data.lightmapFar = redLightMap;
			LightmapSettings.lightmaps = new LightmapData[1]{data};
		}
	}
}

2.场景的烘培贴图已经更新,但是有些物件prefab想运行时加载进来。如果直接Instance的话 这个Prefab是没有烘培信息的。(灰颜色的)
解决这个问题我查到了 一篇大大的文章,它带了例子大家可以下载下来。  http://forum.unity3d.com/threads/problems-with-instantiating-baked-prefabs.324514/#post-2177524

代码在这里,把如下代码挂在GameObject上。当场景烘培结束后,把他保存成prefab,运行的时候直接加载进来就行了。

#if UNITY_EDITOR
using UnityEditor;
using System.IO;
#endif
using UnityEngine;
using System.Collections.Generic;
[DisallowMultipleComponent,ExecuteInEditMode] 
public class PrefabLightmapData : MonoBehaviour
{
    [System.Serializable]
    struct RendererInfo
    {
        public Renderer renderer;
        public int lightmapIndex;
        public Vector4 lightmapOffsetScale;
    }
 
    [SerializeField]
    RendererInfo[] m_RendererInfo;
    [SerializeField]
    Texture2D[] m_Lightmaps;
    [SerializeField]
    Texture2D[] m_Lightmaps2;
 
    const string LIGHTMAP_RESOURCE_PATH = "Assets/Resources/Lightmaps/";
 
    [System.Serializable]
    struct Texture2D_Remap
    {
        public int originalLightmapIndex;
        public Texture2D originalLightmap;
        public Texture2D lightmap0;
        public Texture2D lightmap1;
    }
 
    static List<Texture2D_Remap> sceneLightmaps = new List<Texture2D_Remap>();
 
    void Awake()
    {
        ApplyLightmaps(m_RendererInfo, m_Lightmaps, m_Lightmaps2);
    }
 
    static void ApplyLightmaps(RendererInfo[] rendererInfo, Texture2D[] lightmaps, Texture2D[] lightmaps2)
    {
        bool existsAlready = false;
        int counter = 0;
        int[] lightmapArrayOffsetIndex;
 
        if (rendererInfo == null || rendererInfo.Length == 0)
            return;
 
        var settingslightmaps = LightmapSettings.lightmaps;
        var combinedLightmaps = new List<LightmapData>();
        lightmapArrayOffsetIndex = new int[lightmaps.Length];
 
        for (int i = 0; i < lightmaps.Length; i++)
        {
            existsAlready = false;
            for (int j = 0; j < settingslightmaps.Length; j++)
            {
                if (lightmaps[i] == settingslightmaps[j].lightmapFar)
                {
                    lightmapArrayOffsetIndex[i] = j;
                    existsAlready = true;
                }
            }
 
            if (!existsAlready)
            {
                lightmapArrayOffsetIndex[i] = counter + settingslightmaps.Length;
                var newLightmapData = new LightmapData();
                newLightmapData.lightmapFar = lightmaps[i];
                newLightmapData.lightmapNear = lightmaps2[i];
                combinedLightmaps.Add(newLightmapData);
                ++counter;
            }
        }
 
        var combinedLightmaps2 = new LightmapData[settingslightmaps.Length + counter];
        settingslightmaps.CopyTo(combinedLightmaps2, 0);
 
        if (counter > 0)
        {
            for (int i = 0; i < combinedLightmaps.Count; i++)
            {
                combinedLightmaps2[i + settingslightmaps.Length] = new LightmapData();
                combinedLightmaps2[i + settingslightmaps.Length].lightmapFar = combinedLightmaps[i].lightmapFar;
                combinedLightmaps2[i + settingslightmaps.Length].lightmapNear = combinedLightmaps[i].lightmapNear;
            }
        }
 
        ApplyRendererInfo(rendererInfo, lightmapArrayOffsetIndex);
 
        LightmapSettings.lightmaps = combinedLightmaps2;
    }
 
    static void ApplyRendererInfo(RendererInfo[] infos, int[] arrayOffsetIndex)
    {
        for (int i = 0; i < infos.Length; i++)
        {
            var info = infos[i];
            info.renderer.lightmapIndex = arrayOffsetIndex[info.lightmapIndex];
            info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
        }
    }
 
#if UNITY_EDITOR
    [MenuItem("Assets/Update Scene with Prefab Lightmaps")]
    static void UpdateLightmaps()
    {
        PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
 
        foreach (var instance in prefabs)
        {
            ApplyLightmaps(instance.m_RendererInfo, instance.m_Lightmaps, instance.m_Lightmaps2);
        }
 
        Debug.Log("Prefab lightmaps updated");
    }
 
    [MenuItem("Assets/Bake Prefab Lightmaps")]
    static void GenerateLightmapInfo()
    {
        Debug.ClearDeveloperConsole();
 
        if (Lightmapping.giWorkflowMode != Lightmapping.GIWorkflowMode.OnDemand)
        {
            Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
            return;
        }
 
        Lightmapping.Bake();

		string lightMapPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(),LIGHTMAP_RESOURCE_PATH);

		if(!Directory.Exists(lightMapPath))
			Directory.CreateDirectory(lightMapPath);

        sceneLightmaps = new List<Texture2D_Remap>();
 
        //var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
		var sceneName =Path.GetFileNameWithoutExtension(EditorApplication.currentScene) ; 
		var resourcePath = LIGHTMAP_RESOURCE_PATH + sceneName;
		var scenePath = System.IO.Path.GetDirectoryName(EditorApplication.currentScene) + "/" + sceneName + "/";
		
        PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
 
        foreach (var instance in prefabs)
        {
            var gameObject = instance.gameObject;
            var rendererInfos = new List<RendererInfo>();
            var lightmaps = new List<Texture2D>();
            var lightmaps2 = new List<Texture2D>();
 
            GenerateLightmapInfo(scenePath, resourcePath, gameObject, rendererInfos, lightmaps, lightmaps2);
 
            instance.m_RendererInfo = rendererInfos.ToArray();
            instance.m_Lightmaps = lightmaps.ToArray();
            instance.m_Lightmaps2 = lightmaps2.ToArray();
 
            var targetPrefab = PrefabUtility.GetPrefabParent(gameObject) as GameObject;
            if (targetPrefab != null)
            {
                //Prefab
                PrefabUtility.ReplacePrefab(gameObject, targetPrefab);
            }
 
            ApplyLightmaps(instance.m_RendererInfo, instance.m_Lightmaps, instance.m_Lightmaps2);
        }
 
        Debug.Log("Update to prefab lightmaps finished");
    }
 
    static void GenerateLightmapInfo(string scenePath, string resourcePath, GameObject root, List<RendererInfo> rendererInfos, List<Texture2D> lightmaps, List<Texture2D> lightmaps2)
    {
        var renderers = root.GetComponentsInChildren<MeshRenderer>();
        foreach (MeshRenderer renderer in renderers)
        {
            if (renderer.lightmapIndex != -1)
            {
                RendererInfo info = new RendererInfo();
                info.renderer = renderer;
                info.lightmapOffsetScale = renderer.lightmapScaleOffset;
 
                Texture2D lightmap = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapFar;
                Texture2D lightmap2 = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapNear;
                int sceneLightmapIndex = AddLightmap(scenePath, resourcePath, renderer.lightmapIndex, lightmap, lightmap2);
 
                info.lightmapIndex = lightmaps.IndexOf(sceneLightmaps[sceneLightmapIndex].lightmap0);
                if (info.lightmapIndex == -1)
                {
                    info.lightmapIndex = lightmaps.Count;
                    lightmaps.Add(sceneLightmaps[sceneLightmapIndex].lightmap0);
                    lightmaps2.Add(sceneLightmaps[sceneLightmapIndex].lightmap1);
                }
 
                rendererInfos.Add(info);
            }
        }
    }
 
    static int AddLightmap(string scenePath, string resourcePath, int originalLightmapIndex, Texture2D lightmap, Texture2D lightmap2)
    {
        int newIndex = -1;
 
        for (int i = 0; i < sceneLightmaps.Count; i++)
        {
            if (sceneLightmaps[i].originalLightmapIndex == originalLightmapIndex)
            {
                return i;
            }
        }
 
        if (newIndex == -1)
        {
            var lightmap_Remap = new Texture2D_Remap();
            lightmap_Remap.originalLightmapIndex = originalLightmapIndex;
            lightmap_Remap.originalLightmap = lightmap;
 
            var filename = scenePath + "Lightmap-" + originalLightmapIndex;
 
            lightmap_Remap.lightmap0 = GetLightmapAsset(filename + "_comp_light.exr", resourcePath + "_light", originalLightmapIndex, lightmap);
            if (lightmap2 != null)
            {
                lightmap_Remap.lightmap1 = GetLightmapAsset(filename + "_comp_dir.exr", resourcePath + "_dir", originalLightmapIndex, lightmap2);
            }
 
            sceneLightmaps.Add(lightmap_Remap);
            newIndex = sceneLightmaps.Count - 1;
        }
 
        return newIndex;
    }
 
    static Texture2D GetLightmapAsset(string filename, string resourcePath, int originalLightmapIndex, Texture2D lightmap)
    {
        AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
        var importer = AssetImporter.GetAtPath(filename) as TextureImporter;
        importer.isReadable = true;
        AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
 
        var assetLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(filename);

        var assetPath = resourcePath + "-" + originalLightmapIndex + ".asset";
        var newLightmap = Instantiate<Texture2D>(assetLightmap);
 
        AssetDatabase.CreateAsset(newLightmap, assetPath);
 
        newLightmap = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
 
        importer.isReadable = false;
        AssetDatabase.ImportAsset(filename, ImportAssetOptions.ForceUpdate);
 
        return newLightmap;
    }
#endif
 
}

 

点击 Assets/Bake Prefab Lightmaps 进行烘培, 这样它的脚本里会把index 和 offset保存在prefab里。它还会保存上当前烘培场景的Lightmap,如果运行时想更换的话,你可以加一些自己的逻辑进行切换。

Snip20151223_1

Snip20151223_2

最后欢迎大家测试,欢迎大家提出宝贵意见,我们一起把unity这个坑填了!

下载: http://pan.baidu.com/s/1kTU9EOB

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之Unity5.x运行时动态更新烘培贴图(八十七)》有 44 条评论

  1. lilili说:

    为什么我烘焙成功后,保存完场景然后关闭重新打开,上次烘焙完的场景并没有保存下来??

  2. 玄武说:

    prefab从resources目录加载竟来,可以正常显示光照贴图,但是把prefab制作成assetbundle,lightmap就不能正常恢复了,debug frame 显示LIGHTMAP_OFF。求雨神帮助~

  3. 雨松MOMO说:

    unity5没有这个功能了。我做了一个工具Ctrl shift D 拷贝lightmap烘培贴图。https://bitbucket.org/xuanyusong/unity-duplicatelightmap

  4. unity5没有这个功能了。我做了一个工具Ctrl+ shift +D 拷贝lightmap烘培贴图。https://bitbucket.org/xuanyusong/unity-duplicatelightmap

  5. 落叶说:

    请问下 为什么我在mac里烘培 是有烘培效果的 但是去到真机就没效果了? 有没有什么办法解决? 我用的版本是5.3.4

  6. ljf说:

    在另一个场景烘焙的Prefab影子是落在地上的,这样新场景中使用Prefab时就没有地面影子了。在Prefab下放一个面片接收影子,使用时把面片没有影子的部分透明化,请教一下有办法做到吗?

  7. colnlin说:

    Unity5.3 我试过windows上 mac上都可以,在真机上(android和ios)都不成功 求老师指点

    • 这个方法在真机上可以的。 我们项目现在就在用。。 没问题

      • colnlin说:

        松 我使用的脚基本和上面一样,只是没有每次Lightmapping.Bake(); 因为直接是外包bake好了的,然后我把prefab打包成了bundle,这样在pc上是没有问题的,上真机就不行,这是我也没有bake的问题,还是不能打bundle的问题呢?求指点

  8. 红豆说:

    Unity5.3 在pc上显示一切正常,打包到安卓机上就没效果了,输出的值都是一样的。是不是有什么需要设置的@雨松MOMO大大

  9. 叶月葵说:

    想到个问题,请问,我可以在不使用unity自带的分UV功能的情况下,使用在3D软件里面分好的UV2以及用3D软件来进行光照贴图的烘焙,然后把烘焙好的数据重新在unity里面创建assetdate当作光照贴图使用吗

  10. 黎亚洲说:

    MOMO麻烦问下为什么用你这个同事烘焙多个的时候会重叠在一起然后灯光会自动删掉

  11. 大神求助!为什么美术烘焙好的场景导出package我这里再导入 lightmap信息全都没了 用了你上面的代码将场景全部变成prefab再导出 信息有 但是看到的效果还是不同 这是为什么?

  12. 坚果说:

    难道游戏中真的要放弃烘培吗,如果游戏的关卡里不能使用同一烘培结果,那游戏的体积会无穷增加,没办法用了啊
    难道只能先在3dmax里烘培好吗?
    求松哥指点[疑问]

  13. 第一个例子 最下面有下载地址

  14. 坚果说:

    非常感谢雨松的教程,有个问题想问您。
    5.3后,如果多场景使用同一烘培结果呢,如:关卡设计的时候,绝大部分场景是一样的,就某一个球的位置变了
    完全可以使用同一烘培结果[疑问]

    求指点

  15. bob说:

    Unity5.x烘焙极慢,根本就烘不动,有什么好的解决方案吗?难道非让我退回到4.6?

  16. 说:

    烘培烘培贴图有什么要求吗?

  17. 【C 阿哥】说:

    好。。好。。好。。好。。

  18. 【C++阿哥】说:

    好。。好。。好。。好。。

留下一个回复

你的email不会被公开。 必填项已用 * 标注。