无缝地图涉及到地形、物件的分块加载,同样,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。合并图集用到unity自带的接口, public Rect[] PackTextures(Texture2D[] textures, int padding, int maximumAtlasSize);
public static void CombineRendererInfos2(List<RendererInfo> rdInfoList, out Rect bound, out Rect[] packedRects)
{
packedRects = new Rect[]; //计算总面积
int areaAll = ;
for(int i = ; i < rdInfoList.Count; ++i)
{
areaAll += (rdInfoList[i].MyLightmap.width * rdInfoList[i].MyLightmap.height);
} //计算最接近这个面积的宽高尺寸
int size = ;
while(true)
{
if (size * size > areaAll)
break;
size *= ;
} bound = new Rect(, , size, size); if (areaAll == )
return; while (true)
{
List<Rect> rectList = new List<Rect>();
List<Texture2D> texList = new List<Texture2D>();
rectList.Add(bound);
for (int i = ; i < rdInfoList.Count; ++i)
{
Texture2D tex = new Texture2D(rdInfoList[i].MyLightmap.width, rdInfoList[i].MyLightmap.height);
texList.Add(tex);
} Texture2D combined = new Texture2D((int)bound.width, (int)bound.height, TextureFormat.ARGB32, false); packedRects = combined.PackTextures(texList.ToArray(), , size); if (packedRects == null || packedRects.Length == || packedRects[].width * bound.width < texList[].width)
{
bound.width *= ;
bound.height *= ;
size *= ;
}
else
{
bound.width = combined.width;
bound.height = combined.height;
for (int i = ; i < rdInfoList.Count; ++i)
{
Vector2 pos = new Vector2(packedRects[i].x, packedRects[i].y);
{
rdInfoList[i].Position = pos;
rdInfoList[i].Position.x *= bound.width;
rdInfoList[i].Position.y *= bound.height; //为了消除接缝黑边
var ignorPixels = /1f;
var blockW = rdInfoList[i].MyLightmap.width;
var blockH = rdInfoList[i].MyLightmap.height;
var texWidth = bound.width;
var texHeight = bound.height;
var sourceBounds = rdInfoList[i].OldBound; var scaleUVX = (blockW - ignorPixels) / (texWidth * (sourceBounds.z - sourceBounds.x));
var offsetUVX = (pos.x) / (float)texWidth - scaleUVX * sourceBounds.x; var scaleUVY = (blockH - ignorPixels) / (texHeight * (sourceBounds.w - sourceBounds.y));
var offsetUVY = (pos.y) / (float)texHeight - scaleUVY * sourceBounds.y; rdInfoList[i].MyLightmapScaleInfo = new Vector4(scaleUVX, scaleUVY, offsetUVX, offsetUVY);
}
}
break;
}
}
}
上面重点是合并到新的lightmap后,需要重新计算Renderer的LightmapScaleOffset
var scaleUVX = (blockW) / (texWidth * (sourceBounds.z - sourceBounds.x));
var offsetUVX = pos.x / (float)texWidth - scaleUVX * sourceBounds.x;
 
var scaleUVY = (blockH) / (texHeight * (sourceBounds.w - sourceBounds.y));
var offsetUVY = pos.y / (float)texHeight - scaleUVY * sourceBounds.y
 
rdInfoList[i].MyLightmapScaleInfo = new Vector4(scaleUVX, scaleUVY, offsetUVX, offsetUVY);

实际上就是前面通过uv与LightmapScaleOffset计算一个范围的反推过程,现在是已知最终范围和原始uv,计算新的LightmapScaleOffset,原理还是很简单的。
 

无限大地图:lightmap拆分的更多相关文章

  1. Unity 场景分页插件 World Streamer 支持无限大地图的解决方案(二)

    Terrain Streaming 可以用WorldCreator创建Tile地形,然后用WorldStreamer实现分块地图.比如10000*10000(16平方公里) 的地形,需要1000*10 ...

  2. 《MFC游戏开发》笔记七 游戏特效的实现(一):背景滚动

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9344721 作者:七十一雾央 新浪微博:http:// ...

  3. 百度王一男: DevOps 的前提是拆掉业务-开发-测试-运维中间的三面墙

    这是一个创建于 375 天前的主题,其中的信息可能已经有所发展或是发生改变. 由数人云.优维科技.中生代社区联合发起的 系列 Meetup < DevOps&SRE 超越传统运维之道&g ...

  4. HBase性能优化完全版

    近期在处理HBase的业务方面常常遇到各种瓶颈,一天大概一亿条数据,在HBase性能调优方面进行相关配置和调优后取得了一定的成效,于是,特此在这里总结了一下关于HBase全面的配置,主要参考我的另外两 ...

  5. supermap iobect .net 7.1.2 图例的拆分

    LayoutSelection objLytSelect = m_MapLayoutControl.MapLayout.Selection;//.Selection; //LayoutSelectio ...

  6. 首师大附中互测题:50136142WXY的坑爹百度地图【B006】(可以喝的超大桶水)

    [B006]50136142WXY的坑爹百度地图[难度B]——————————————————————————————————————————————————————————————————————— ...

  7. Android中调用百度地图

    一.调用百度地图 --第一种方法 1.下载百度地图SDK SDK可以拆分下载,需要使用那一部分功能就下载相应包含的SDK,如下图 核心的的jar 和so包,放在工程中的libs目录下 2.申请key ...

  8. 《MFC游戏开发》笔记十 游戏中的碰撞检测进阶:地图类型&障碍物判定

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9394465 作者:七十一雾央 新浪微博:http:// ...

  9. js调用百度地图接口

    原文:js调用百度地图接口 这是前几天公司做的新项目,上面需要用到地图的数据.第一次做这类型的东西没啥思路,咱们经理说,这东西简单,截个图存文件夹里调整好尺寸,数据库里存上图片的地址动态调用就行了.心 ...

随机推荐

  1. 201521123045 《Java程序设计》第7周学习总结

    Java 第七周总结 1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Obj ...

  2. sublime text 按下Ctrl + B 显示空白的解决办法

    环境:windows xp , sublime text 2 条件:环境变量也已经设置好,可是无论怎么编译都是空白,如图: 于是鄙人百度之,发现不少网友都有这样的问题,但是网上给出的的解决方案千篇一律 ...

  3. Java课程设计-定时器

    Java课程设计--定时器 1.团队课程设计博客链接 团队博客地址 2.个人负责模块或任务说明 框架构建 时间设置面板,倒计时面板 按钮设置 3.自己的代码提交记录截图 4.自己负责模块或任务详细说明 ...

  4. 201521123074 《Java程序设计》第14周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 Q1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现 ...

  5. 第二部分----CSS的基础语法

    PART-1 CSS的基础常识 一.什么是CSS? W3C标准中,倡导有3:其一为内容与表现分离,其二为内容与行为分离,其三为内容结构的语义化.其倡导中第一条的"表现"指的便可以说 ...

  6. Atom的追踪函数插件和自定义语法

    atom网盘链接:http://pan.baidu.com/s/1nvt7vJz 密码:om26 追踪函数插件 直接使用最新autom版本傻瓜式安装 名字:goto-definition 地址:htt ...

  7. HttpServletRequest获取URL、URI

    从Request对象中可以获取各种路径信息,以下例子: 假设请求的页面是index.jsp,项目是WebDemo,则在index.jsp中获取有关request对象的各种路径信息如下 import j ...

  8. pycharm 2017新建文件添加编码方式等

    file->setting->Editor->File and Code Templates->Python Script 添加 #!/usr/bin/python3# -*- ...

  9. Azure Powershell使用已有Image创建ARM非托管磁盘虚拟机

    生成Image映像文件,记录好Image的URL(下面URL为测试URL,具体请参考实际):ImageURL:https://hlmrgstoragen.blob.core.chinacloudapi ...

  10. 在JavaScript中使用json.js:Ajax项目之POST请求(异步)

    经常在百度搜索框输入一部分关键词后,弹出候选关键热词.现在我们就用Ajax技术来实现这一功能. 一.下载json.js文件 百度搜一下,最好到json官网下载,安全起见. 并与新建的两个文件部署如图 ...