unity中光照贴图(lightmapping)的存储方式比较特殊,烘焙好的光照贴图存储在场景中,而参与烘焙的物体本身不记录光照贴图,只记录一组uv数据对应光照贴图。在显示的时候,shader通过这组uv坐标访问物体的光照贴图信息,再显示出来。 并且这些光照贴图没法用手动编辑,只能写脚本编辑。
而相对应的如果不在unity中烘焙,而在maya中烘焙,就是一张lightmapping放在shader的自发光贴图里面了。
所以问题在于,在unity中一个场景里烘焙的物体很难带着光照贴图放到另一个场景里。如果我们非要这么干,就得给被烘焙的Prefab加个脚本,记录光照贴图,在加入其它场景中重新加载光照贴图。
1. 光照贴图访问
首先是要访问一下Prefab里面所有子物体,从根节点遍历一下这个树就可以了,非常简单就不提了。
烘焙过的物体会有一个光照贴图的uv信息,以及所使用的光照贴图ID值。脚本中用物体的Render
组件的lightmapIndex
和lightmapScaleOffset
属性访问,lightmapScaleOffset
是Vector4
类型,xyzw分别是Tiling X, Tiling Y, Offset X, Offset Y。比如:
1 2 3 |
int thisLightmapIndex = currentGameobject.GetComponent<Renderer>().lightmapIndex; Vector4 thisScaleOffset = currentGameobject.GetComponent<Renderer>().lightmapScaleOffset; |
而场景中存储的所有光照贴图用LightmapSettings
这个类访问,典型使用方式如下:
1 2 |
LightmapData[] sceneLightmap = LightmapSettings.lightmaps; |
LightmapData
这个类会存两个属性,lightmapFar
和lightmapNear
,类型是texture2D
,也就是那张光照贴图了。
2. 贴图信息存储
我们要转移光照贴图先要记录光照贴图,信息记录在哪呢?首先想到直接存在脚本的类里,但是不可以,因为在物体放入新场景时会初始化这个脚本类,没法存下来。那么如果搞一个全局变量呢?或者一个静态类存储?这也不行,毕竟这样还是把信息存在内存里了,关闭程序再次打开信息就没有了。所以还得把信息存到硬盘上呀。
我们当然可以搞一些小手段把信息序列化存储在硬盘上,但好消息是unity提供了一个类ScriptableObject
专门应对这种情况,可以参见unity官方参考的这一节. Unity也给了个视频教程.
需要注意的是,自己写一个继承ScriptableObject
的类时,字段都需要是Serializable的,所以想直接存光照贴图有点困难,因为LightmapData[]
没法序列化。也说不清定有方法把LightmapData
序列化,笔者反正采用了把光照贴图的贴图存下来的方式。所以这个存信息的类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class LightmapTransferHolder : ScriptableObject { public LightmapData[] lightmapdata; public List<Texture2D> lightmapTex; public List<LightmapPara> lightmapInfo; } [System.Serializable] public class LightmapPara { public int index; public Vector4 ScaleOffset; } |
这个类初始化的时候:
LightmapTransferHolder dataHolder = ScriptableObject.CreateInstance<LightmapTransferHolder>();
存储的时候:
AssetDatabase.CreateAsset(holder, pathname);
读取的时候:
LightmapTransferHolder holder = (LightmapTransferHolder) AssetDatabase.LoadAssetAtPath(pathname, typeof(LightmapTransferHolder));
就可以啦
3. 光照贴图读取
如果想让Prefab载入的时候就向场景里加载信息,需要加一个[ExecuteInEditMode]
的属性,之后在重载一个Awake()
函数,unity自动处理事件消息。
之后需要判断场景里是不是已经有了我们想加载的光照贴图,并对应上我们的光照贴图信息就好了。不要忘了销毁的时候删除使用的光照贴图,重载一个OnDestroy()
函数。
以上基本就是要点,刚开始接触unity,请多指教。