无限大地图:lightmap拆分

来源:互联网 发布:centos 7.3 安装vftp 编辑:程序博客网 时间:2024/05/10 11:07
无缝地图涉及到地形、物件的分块加载,同样,lightmap也需要动态加载。而场景烘焙时,所有物件都是一起烘焙的,那怎么把某些物件指定烘焙到某一张lightmap贴图中?网上找了很久,也没有看到具体的实现方式,还是要自己从头造车,结合网上的一些理论,经过实践,分享2个方法
 
1. 设置自定义LightmapParameters,设置Bake tag,相同tag的物件,会烘焙到同一张lightmap中。Terrain_1_1是新创建的LightmapParameters,替换掉默认的Pamameters
 
 
2. 通过计算Renderer的uv,从完整Lightmap贴图中抠出来指定某个范围的贴图,然后与想要合并到一起的其他贴图生成一个新的lightmap贴图
 
第一个方法是最省事的,但是有一个问题,LightmapParameters看起来只有手动设置,Unity没有提供api,貌似不能用代码来实现自动化。那么只能使用黑科技了(反编译UnityEditor.dll得来的方法)
复制代码
 //创建LightmapParameters资源文件        public static void CreateLightmapParameterFile(string path, int bakeTag)        {            LightmapParameters lp = new LightmapParameters();            lp.bakedLightmapTag = bakeTag;            AssetDatabase.CreateAsset(lp, path);            AssetDatabase.ImportAsset(path);        }        public static void SetRenderLightmapParameters(Renderer renderer, string giparamsFile)        {            SerializedObject so = new SerializedObject(renderer);            var sp = so.FindProperty("m_LightmapParameters");            sp.objectReferenceValue = AssetDatabase.LoadAssetAtPath(giparamsFile, typeof(LightmapParameters));            so.ApplyModifiedProperties();        }
重点在so.FindProperty("m_LightmapParameters");这是一个不对外的成员变量,通过反射来获取,然后设置objectReferenceValue ,关联之前创建的LightmapParameters文件。后面就简单了,自己实现吧
复制代码

 

  下面是第二个方法,Renderer里面有个变量lightmapScaleOffset,这个变量记录了光照uv的缩放和偏移量,通过计算可以得到该Renderer在lightmap完整贴图中的范围,然后就能扣出来
  这里总结一下流程
  a:通过uv2和Renderer的LightmapScaleOffset计算Lightmap贴图中的占用范围(bound)
  b:抠图,打包图集,获得新的范围(bound)
  c:已知新的bound和原始uv2,计算新的LightmapScaleOffset
 
复制代码
//获取uv2        public static Vector2[] GetMeshUV2(Mesh m)        {    //如果不存在uv2,则使用uv代替            var uv2 = m.uv2;            if (uv2 == null || uv2.Length == 0)                uv2 = m.uv;            return uv2;        }       //计算uv的范围        public static Vector4 GetBounds(Vector2[] uv, Renderer r)        {            if (uv != null)            {                var __uv = new Vector2[uv.Length];                Array.Copy(uv, __uv, uv.Length);                uv = __uv;                var minx = float.MaxValue;                var miny = float.MaxValue;                var maxx = float.MinValue;                var maxy = float.MinValue;                for (var _j = 0; _j < uv.Length; ++_j)                {                    var _uv = uv[_j];                    if (_uv.x < minx)                    {                        minx = _uv.x;                    }                    if (_uv.y < miny)                    {                        miny = _uv.y;                    }                    if (_uv.x > maxx)                    {                        maxx = _uv.x;                    }                    if (_uv.y > maxy)                    {                        maxy = _uv.y;                    }                }                var bounds = new Vector4(minx, miny, maxx, maxy);                return bounds;            }            return Vector4.zero;        }//通过与LightmapScaleOffset计算出原始uv范围对应Lightmap贴图中的范围        public static Vector4 CalcBoundWithLightmapScaleOffset(Vector4 sourceBounds, Vector4 lightmapScaleOffset)        {            var scaleBounds = new Vector4(sourceBounds.x * lightmapScaleOffset.x + lightmapScaleOffset.z,                                     sourceBounds.y * lightmapScaleOffset.y + lightmapScaleOffset.w,                                     sourceBounds.z * lightmapScaleOffset.x + lightmapScaleOffset.z,                                     sourceBounds.w * lightmapScaleOffset.y + lightmapScaleOffset.w);            return scaleBounds;        }//这里就是扣图了public static Texture2D PickTexture(Texture2D sourceTex, Vector4 bounds)        {            var blockW = (int)((bounds.z - bounds.x) * sourceTex.width);            var blockH = (int)((bounds.w - bounds.y) * sourceTex.height);            int startX = (int)(bounds.x * sourceTex.width);            int startY = (int)(bounds.y * sourceTex.height);            //startY = (tex.height - startY - blockH);            if (blockH == 0 || blockW == 0)                return null;            var colors = sourceTex.GetPixels(startX, startY, blockW, blockH);            Texture2D tex2d = new Texture2D(blockW, blockH);            tex2d.SetPixels(colors);            tex2d.Apply();            return tex2d;        }//从整lightmap贴图中扣指定物件的光照贴图        public static Texture2D PickLightmap(GameObject go, out Vector4 bound)        {            bound = Vector4.zero;            if (go == null)                return null;            var meshFilter = go.GetComponent<MeshFilter>();            var renderer = go.GetComponent<Renderer>();            if (meshFilter == null || renderer == null)                return null;            var tex = GetFullLightmap(renderer);            var uv2s = GetMeshUV2(meshFilter.sharedMesh);            //var bounds = GetBounds(uv2s, renderer);            var sourceBounds = LightMapUtil.GetBounds(uv2s, renderer);            var scaleBounds = CalcBoundWithLightmapScaleOffset(sourceBounds, renderer.lightmapScaleOffset);            bound = sourceBounds;            return PickTexture(tex, scaleBounds);        }
复制代码

 

以上代码,重点是uv2和LightmapScaleOffset的计算,算出一个矩形范围。这里把图扣出来了,然后是把若果抠出来的图合并到一个新的Texture,要用到矩形排序算法,因为时间关系,这个算法我也没有深入了解,所以写了一个很简陋的算法,待优化

【本文由“科技有点”发布,2017年10月21日】

原创粉丝点击