前段时间我研究过这个问题,但是没有解决只好作罢。今天刚好有人又问我这个问题,我得空查了一下还是找到了解决办法。另外也感谢问我的人,解答问题的同时也是我学习的过程。
运行时更新烘培贴图分两种情况
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,如果运行时想更换的话,你可以加一些自己的逻辑进行切换。
最后欢迎大家测试,欢迎大家提出宝贵意见,我们一起把unity这个坑填了!
下载: http://pan.baidu.com/s/1kTU9EOB
- 本文固定链接: https://www.xuanyusong.com/archives/3807
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!



为什么我烘焙成功后,保存完场景然后关闭重新打开,上次烘焙完的场景并没有保存下来??
prefab从resources目录加载竟来,可以正常显示光照贴图,但是把prefab制作成assetbundle,lightmap就不能正常恢复了,debug frame 显示LIGHTMAP_OFF。求雨神帮助~
我也是,把prefab制作成assetbundle,lightmap就不能正常显示了,但是LightmapSetting里已经正常显示烘焙贴图的信息了。
我也遇到这样的问题了,请问解决了吗
我也想问这个问题,有没有解决了的大哥
unity5没有这个功能了。我做了一个工具Ctrl shift D 拷贝lightmap烘培贴图。https://bitbucket.org/xuanyusong/unity-duplicatelightmap
unity5没有这个功能了。我做了一个工具Ctrl+ shift +D 拷贝lightmap烘培贴图。https://bitbucket.org/xuanyusong/unity-duplicatelightmap
请问下 为什么我在mac里烘培 是有烘培效果的 但是去到真机就没效果了? 有没有什么办法解决? 我用的版本是5.3.4
我也遇到了同样的问题,求指导!
请问下 为什么我在mac里烘培 是有烘培效果的 但是去到真机就没效果了? 有没有什么办法解决? 我用的版本是5.3.4我也遇到了同样的问题,求指导
在另一个场景烘焙的Prefab影子是落在地上的,这样新场景中使用Prefab时就没有地面影子了。在Prefab下放一个面片接收影子,使用时把面片没有影子的部分透明化,请教一下有办法做到吗?
prefab上带不了影子 除非用时时光 算 。。 如果prefab不发生位移的话, 你可以考虑运行时更新烘培贴图
Unity5.3 我试过windows上 mac上都可以,在真机上(android和ios)都不成功 求老师指点
这个方法在真机上可以的。 我们项目现在就在用。。 没问题
松 我使用的脚基本和上面一样,只是没有每次Lightmapping.Bake(); 因为直接是外包bake好了的,然后我把prefab打包成了bundle,这样在pc上是没有问题的,上真机就不行,这是我也没有bake的问题,还是不能打bundle的问题呢?求指点
Unity5.3 在pc上显示一切正常,打包到安卓机上就没效果了,输出的值都是一样的。是不是有什么需要设置的@雨松MOMO大大
想到个问题,请问,我可以在不使用unity自带的分UV功能的情况下,使用在3D软件里面分好的UV2以及用3D软件来进行光照贴图的烘焙,然后把烘焙好的数据重新在unity里面创建assetdate当作光照贴图使用吗
这样不行。。unity没提供这样的接口
实际测试了一下,好像没办法获得模型和光照贴图的对应关系,必须烘焙了之后才能得到一个对应关系,倒是烘焙之后可以吧自己另外烘焙得贴图当做光照贴图插进来
怎么插,5.3烘培的贴图选择键都被锁定了,就是3D软件烘培的光照贴图怎么连接到模型上,整体UV摆放的位置也会被移动或者重新组入别的部件了。
MOMO麻烦问下为什么用你这个同事烘焙多个的时候会重叠在一起然后灯光会自动删掉
大神求助!为什么美术烘焙好的场景导出package我这里再导入 lightmap信息全都没了 用了你上面的代码将场景全部变成prefab再导出 信息有 但是看到的效果还是不同 这是为什么?
lightmap的信息是记录在scene里的。只要保存了导出就会有的
恩 感谢!后来在PC端可以使用了 但是导出apk的时候 烘焙的还是没有显示。。
难道游戏中真的要放弃烘培吗,如果游戏的关卡里不能使用同一烘培结果,那游戏的体积会无穷增加,没办法用了啊
难道只能先在3dmax里烘培好吗?
求松哥指点[疑问]
如果你想复制就试试我上面的这个脚本吧。。 用脚本里的烘培方法烘培就可以了
好呢,我试试先
第一个例子 最下面有下载地址
祝新年快乐,刚看到你同步在发这个了,求指点
看到了,第1个例子,是吧,谢谢啊!
非常感谢雨松的教程,有个问题想问您。
5.3后,如果多场景使用同一烘培结果呢,如:关卡设计的时候,绝大部分场景是一样的,就某一个球的位置变了
完全可以使用同一烘培结果[疑问]
求指点
目前,我新建场景之后,然后把烘培好之后的prefab抛进来,烘培结果并没有用
即使是在lightmaps里手动指定,也没效果。[泪]
对啊 u5和u4不一样, 烘培完不能直接复制。 。
Unity5.x烘焙极慢,根本就烘不动,有什么好的解决方案吗?难道非让我退回到4.6?
分辨率哪个参数写小一点
[嘻嘻] 买个mac pro远程烘焙
烘培烘培贴图有什么要求吗?
好。。好。。好。。好。。
[威武]
地形组件不行,,
不对啊, 我试了一下 是可以的啊。
好。。好。。好。。好。。