打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Unity5中lightmap的坑
Unity5中光照系统替换为Enlighten是非常大的革新。但是对手游来说,好处还未享受到,坑先踩上了。并且是我研究了两天都没有很好的解决办法的深坑。
我并没有系统的学过图形学,所以以下所说的内容都是自己的理解,可能存在错误的地方,敬请见谅。
所谓lightmap,就是用一组预先烘焙好的贴图来替代运行时光影计算。在Unity5之前使用的是beast系统,Unity5使用的是enlighten系统,新系统的好处是支持运行时光照计算,支持全局光。
作为使用者来说就是两个部分,一个是烘焙的部分,一个是加载的部分。
Unity5的lighting分页中有两个勾选,一个是precompute GI,一个是baked GI。一般来说手游还是无法承载precompute的计算量,从效率的角度考虑这两个GI系统在一个场景中只开启一个。我们使用baked GI,其实也就跟之前的光照系统差不多了。另外烘焙的过程真心慢(万一同时勾选了两个GI,那么就是慢上加慢),GI Cache文件夹如果不限制大小的话,动辄几十G,可见其计算量。
烘焙如果勾上auto则所有的烘焙结果存在GI Cache文件夹下,一般这种模式没有什么价值。点击Bake按钮进行烘焙,烘焙完成后会生成跟场景同名的文件夹,里面有光照贴图和一个lightmapsnapshot.asset文件。这个文件就是第一个坑。
光照贴图的加载原理其实很简单,每个renderer中记录了一个lightmapindex和lightmapscaleoffset,LightmapSetting中有一个全局的光照贴图的数组,包含当前场景中所有光照贴图的索引。每个renderer根据index和offset确定自己应该使用光照贴图的哪个部分,最终渲染出实际带光影的效果。
原本这些数据是存在每个renderer里面的,也即存在scene或者是prefab中。但是Unity5为了多场景合作编辑,把这些数据移到snapshot文件中了。场景中不再保存这些信息,snapshot中保存了场景中的renderer对应的光照数据是什么。但是Unity并没有提供访问snapshot的方法。所以原本很简单的问题在这里变得非常恶心。
由于snapshot中保存的是当前场景中的renderer的信息,所以拷贝新的renderer或者在代码中实例化一个物体都是没有光照信息的。如果场景中的物体保存为prefab,则光照信息也会丢失,因为此时场景中关联的是一个prefab文件,而场景物件是保存在prefab中的。
补充说明,snapshot中根据GameObject的udid来保存对应的光照信息。所以只要udid不变,则光照信息正常,只要改变,则光照信息丢失。所以新实例化的物体是没有光照信息的,这个要自己手动设置(其实也很简单,把原物体所有Renderer中的光照信息赋值给新物体中对应的Renderer就好)。 如果烘焙的时候物体不是Prefab,后来保存为Prefab,或者原来烘焙的时候是Prefab,后来取消Prefab的关联,这两个操作都会使光照信息丢失。
一个简单的解决办法是将光照信息保存在一个组件上面,然后加载场景或者物体的时候再恢复。代码如下:
PrefabLightmapDataEditor.cs
[csharp] view plaincopy
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class PrefabLightmapDataEditor : Editor
{
// 把renderer上面的lightmap信息保存起来,以便存储到prefab上面
[MenuItem("GameObject/Lightmap/Save", false, 0)]
static void SaveLightmapInfo ()
{
GameObject go = Selection.activeGameObject;
if (go == null) return;
PrefabLightmapData data = go.GetComponent<PrefabLightmapData>();
if (data == null) {
data = go.AddComponent<PrefabLightmapData>();
}
data.SaveLightmap();
EditorUtility.SetDirty(go);
}
// 把保存的lightmap信息恢复到renderer上面
[MenuItem("GameObject/Lightmap/Load", false, 0)]
static void LoadLightmapInfo()
{
GameObject go = Selection.activeGameObject;
if (go == null) return;
PrefabLightmapData data = go.GetComponent<PrefabLightmapData>();
if (data == null) return;
data.LoadLightmap();
EditorUtility.SetDirty(go);
new GameObject();
}
[MenuItem("GameObject/Lightmap/Clear", false, 0)]
static void ClearLightmapInfo()
{
GameObject go = Selection.activeGameObject;
if (go == null) return;
PrefabLightmapData data = go.GetComponent<PrefabLightmapData>();
if (data == null) return;
data.m_RendererInfo.Clear();
EditorUtility.SetDirty(go);
}
}
PrefabLightmapData.cs[csharp] view plaincopy
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Rendering;
public class PrefabLightmapData : MonoBehaviour
{
[System.Serializable]
public struct RendererInfo
{
public Renderer     renderer;
public int          lightmapIndex;
public Vector4      lightmapOffsetScale;
}
public List<RendererInfo> m_RendererInfo;
void Awake ()
{
LoadLightmap();
Debug.Log("--------------------------------------------");
var renderers = GetComponentsInChildren<MeshRenderer>();
foreach (var item in renderers) {
Debug.Log(item.lightmapIndex);
}
//      if (m_RendererInfo == null || m_RendererInfo.Count == 0)
//          return;
//
//      var lightmaps = LightmapSettings.lightmaps;
//      var combinedLightmaps = new LightmapData[lightmaps.Length + m_Lightmaps.Count];
//
//      lightmaps.CopyTo(combinedLightmaps, 0);
//      for (int i = 0; i < m_Lightmaps.Count; i++)
//      {
//          combinedLightmaps[i+lightmaps.Length] = new LightmapData();
//          combinedLightmaps[i+lightmaps.Length].lightmapFar = m_Lightmaps[i];
//      }
//
//      ApplyRendererInfo(m_RendererInfo, lightmaps.Length);
//      LightmapSettings.lightmaps = combinedLightmaps;
}
public void SaveLightmap()
{
m_RendererInfo.Clear();
var renderers = GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer r in renderers) {
if (r.lightmapIndex != -1) {
RendererInfo info = new RendererInfo();
info.renderer = r;
info.lightmapOffsetScale = r.lightmapScaleOffset;
info.lightmapIndex = r.lightmapIndex;
//                Texture2D lightmap = LightmapSettings.lightmaps[r.lightmapIndex].lightmapFar;
//
//                info.lightmapIndex = m_Lightmaps.IndexOf(lightmap);
//                if (info.lightmapIndex == -1) {
//                    info.lightmapIndex = m_Lightmaps.Count;
//                    m_Lightmaps.Add(lightmap);
//                }
m_RendererInfo.Add(info);
}
}
}
public void LoadLightmap()
{
if (m_RendererInfo.Count <= 0) return;
foreach (var item in m_RendererInfo) {
item.renderer.lightmapIndex = item.lightmapIndex;
item.renderer.lightmapScaleOffset = item.lightmapOffsetScale;
}
}
}
场景中的光照贴图的设置跟之前版本一样,直接设置LightmapSetting中的lightmap数组就可以了,光照贴图同样可以打包成assetbundle,只要能够正确的加载并设置就可以。            如果我们有做地图动态生成、动态加载等需求,那么就必须要自己处理lightmap的加载。大体思路是将场景中物体的光照信息(lightmapindex等)和当前场景的光照贴图(lightmapsetting中获取)保存成一个配置,然后运行时自己加载这些信息。不过这样做有一个前提是场景中的物体一定不能做成prefab,原因如前文所述。
如果不考虑场景的动态更新,那么就简化很多。一个场景一个Scene,每个场景自己烘焙,然后使用LoadLevelAdditive加载场景就可以了。Unity可以正确处理好光照贴图的合并和索引的更新。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
记一次Unity光照贴图移动平台不生效解决之道
?【厚积薄发】揭开Unity AssetBundle庐山真面目(二)
【转载】Unity3D美术方面贴图蓄力帖
Unity3D自带功能:灯光及光照烘焙
Unity的光照烘焙技术(上)
Unity5 新功能解析
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服