Unity3D研究院之Unity5.x运行时动态更新烘培贴图(八十七)
前段时间我研究过这个问题,但是没有解决只好作罢。今天刚好有人又问我这个问题,我得空查了一下还是找到了解决办法。另外也感谢问我的人,解答问题的同时也是我学习的过程。
运行时更新烘培贴图分两种情况
1、场景的物件没有发生变化(也就是说没有运行时加载在场景上的Prefab)此时可以直接更换烘培贴图。
usingUnityEngine;
usingSystem.Collections;
public class NewBehaviourScript : MonoBehaviour {
//烘培烘培贴图1
public Texture2DgreenLightMap;
//烘培贴图2
public Texture2DredLightMap;
void OnGUI()
{
if(GUILayout.Button("green"))
{
LightmapDatadata = new LightmapData();
data.lightmapFar = greenLightMap;
LightmapSettings.lightmaps = new LightmapData[1]{data};
}
if(GUILayout.Button("red"))
{
LightmapDatadata = 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,运行的时候直接加载进来就行了。
usingUnityEngine;
usingSystem.Collections;
usingSystem.Collections.Generic;
public class PrefabLightmapData : MonoBehaviour
{
[System.Serializable]
struct RendererInfo
{
public Rendererrenderer;
public int lightmapIndex;
public Vector4lightmapOffsetScale;
}
[SerializeField]
RendererInfo[] m_RendererInfo;
[SerializeField]
Texture2D[] m_Lightmaps;
void Awake ()
{
if (m_RendererInfo == null || m_RendererInfo.Length == 0)
return;
var lightmaps = LightmapSettings.lightmaps;
var combinedLightmaps = new LightmapData[lightmaps.Length + m_Lightmaps.Length];
lightmaps.CopyTo(combinedLightmaps, 0);
for (int i = 0; i < m_Lightmaps.Length;i++)
{
combinedLightmaps[i+lightmaps.Length] = new LightmapData();
combinedLightmaps[i+lightmaps.Length].lightmapFar = m_Lightmaps[i];
}
ApplyRendererInfo(m_RendererInfo, lightmaps.Length);
LightmapSettings.lightmaps = combinedLightmaps;
}
static void ApplyRendererInfo (RendererInfo[] infos, int lightmapOffsetIndex)
{
for (int i=0;i<infos.Length;i++)
{
var info = infos[i];
info.renderer.lightmapIndex = info.lightmapIndex + lightmapOffsetIndex;
info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
}
}
#if UNITY_EDITOR
[UnityEditor.MenuItem("Assets/Bake Prefab Lightmaps")]
static void GenerateLightmapInfo ()
{
if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.OnDemand)
{
Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
return;
}
UnityEditor.Lightmapping.Bake();
PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
foreach (var instancein prefabs)
{
var gameObject = instance.gameObject;
var rendererInfos = new List<RendererInfo>();
var lightmaps = new List<Texture2D>();
GenerateLightmapInfo(gameObject, rendererInfos, lightmaps);
instance.m_RendererInfo = rendererInfos.ToArray();
instance.m_Lightmaps = lightmaps.ToArray();
var targetPrefab = UnityEditor.PrefabUtility.GetPrefabParent(gameObject) as GameObject;
if (targetPrefab != null)
{
//UnityEditor.Prefab
UnityEditor.PrefabUtility.ReplacePrefab(gameObject, targetPrefab);
}
}
}
static void GenerateLightmapInfo (GameObjectroot, List<RendererInfo> rendererInfos, List<Texture2D> lightmaps)
{
var renderers = root.GetComponentsInChildren<MeshRenderer>();
foreach (MeshRendererrendererin renderers)
{
if (renderer.lightmapIndex != -1)
{
RendererInfoinfo = new RendererInfo();
info.renderer = renderer;
info.lightmapOffsetScale = renderer.lightmapScaleOffset;
Texture2Dlightmap = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapFar;
info.lightmapIndex = lightmaps.IndexOf(lightmap);
if (info.lightmapIndex == -1)
{
info.lightmapIndex = lightmaps.Count;
lightmaps.Add(lightmap);
}
rendererInfos.Add(info);
}
}
}
#endif
}
点击 Assets/Bake Prefab Lightmaps 进行烘培, 这样它的脚本里会把index 和 offset保存在prefab里。它还会保存上当前烘培场景的Lightmap,如果运行时想更换的话,你可以加一些自己的逻辑进行切换。
最后欢迎大家测试,欢迎大家提出宝贵意见,我们一起把unity这个坑填了!