前段时间我研究过这个问题,但是没有解决只好作罢。今天刚好有人又问我这个问题,我得空查了一下还是找到了解决办法。另外也感谢问我的人,解答问题的同时也是我学习的过程。
运行时更新烘培贴图分两种情况
1、场景的物件没有发生变化(也就是说没有运行时加载在场景上的Prefab)此时可以直接更换烘培贴图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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,运行的时候直接加载进来就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
#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程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
MOMO,挂上脚本烘焙完把prefab做成assetbundle,放到其它项目里加载出来不显示lightmap,想问一下这个ab包里是不是不包含烘焙贴图啊?
我的情况是把Prefab做成Assetbundle,加载后设置好对应的LightMapIndex和LightMapScaleOffset以及LightmapSetting。但是LightMap不显示。最后发现是由于Shader打包时,Unity自动将LightMap的部分裁掉了。你们可以试试在Edit/Project Setting/Graphics里把Shader stripping的Lightmap modes设置成Manual后再打包看看好了没。