首页 >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. kk说:

    MOMO,挂上脚本烘焙完把prefab做成assetbundle,放到其它项目里加载出来不显示lightmap,想问一下这个ab包里是不是不包含烘焙贴图啊?

  2. Bay说:

    我的情况是把Prefab做成Assetbundle,加载后设置好对应的LightMapIndex和LightMapScaleOffset以及LightmapSetting。但是LightMap不显示。最后发现是由于Shader打包时,Unity自动将LightMap的部分裁掉了。你们可以试试在Edit/Project Setting/Graphics里把Shader stripping的Lightmap modes设置成Manual后再打包看看好了没。

留下一个回复

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