惜颜705 发表于 2021-1-17 14:52

Unity 大地形加载研究

随着吃鸡手游的火爆,大世界、大地形类的手游正在变得越来越火热。想要大世界观的直面感受,大地形的研究课题也提上开发日程。我们都知道unreal 引擎直接提供了Level Streaming Volume方式的大地形加载方式,无需要过多的代码,就可以动态的生成大场景了。本文主要介绍unity 引擎下自己实现的大地形加载方式。


Unity 大地形研究
*github 对应的地址,点击这里.
针对美术的大世界场景进行分块切割动态策略加载分块地形地形动态生成assetbundle分块之后,lightmap的索引和偏移重新计算
切割大地形

打开 unity, 在菜单栏点击 Terrain->Slicing 即可以切割大地形,代码会自动遍历 Hirerachy 里的地形,切割4X4的16块,切割好的地形默认会存在 Resources 目录下,生成一个地形 gameobject 同名的文件夹。
除了地形分片资源,这里还会生成地形和物件相关的数据信息,保存成二进制文件,保存在同一目录下。
二进制记录的内容代码如下:
FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
BinaryWriter writer = new BinaryWriter(fs);

//这里我分割的宽和长度是一样的.这里求出循环次数,TerrainLoad.SIZE要生成的地形宽度,长度相同
//高度地图的分辨率只能是2的N次幂加1,所以SLICING_SIZE必须为2的N次幂
int size = (int)terrain.terrainData.size.x / trnconst.SLICE;
Vector3 pos = terrain.transform.position;
writer.Write(pos.x);
writer.Write(pos.y);
writer.Write(pos.z);
writer.Write(size);
writer.Write(terrain.treeDistance);
writer.Write(terrain.treeBillboardDistance);
writer.Write(terrain.treeCrossFadeLength);
writer.Write(terrain.treeMaximumFullLODCount);
writer.Write(terrain.detailObjectDistance);
writer.Write(terrain.detailObjectDensity);
writer.Write(terrain.heightmapPixelError);
writer.Write(terrain.heightmapMaximumLOD);
writer.Write(terrain.basemapDistance);
writer.Write(terrain.lightmapIndex);
writer.Write(terrain.castShadows);
WriteParts(writer);
writer.Flush();
writer.Close();
fs.Close();
/div>



分段加载地形和物件

点击 Terrain->Load 会加载分片地形, 并且根据地形分片生成一个对应的 collider.


类似图片的展示的一样,走进走出collider 的 triger 就会触发地形的加载和卸载,实现过程类似于 Unreal引擎实现的Level Streaming Volume。为了避免玩家在collider边界频繁的走动从而出发频繁的内存的加载和卸载,可以在卸载加载的时候加一定的延时。


<div class="highlight">private void OnTriggerEnter(Collider other)
{
    TerrainLoadMgr.sington.LoadItem(x, y);
}


private void OnTriggerExit(Collider other)
{
    TerrainLoadMgr.sington.UnloadItem(x, y);
}

部件的加载

部件如场景里的石头这个物件,他的加载跟着地形分片一同加载、卸载。而不是以 player 为中心做四叉树来管理场景的加载卸载。
项目中引用了第三人称控制器,可以使用W、A、D、S快捷键在场景中走动看看地形动态加载的效果。


lightmap生成assetbundle

点击 Terrain->生成lightmap资源,即可以把当前场景的lightmap 贴图全部达成assetbundle. 在打包lightmap贴图的同时,会生成一个二进制文件被打到同一个assetbundle中,这个二进制文件记录了当前场景里所有render的lightmap的index索引和offsetscale偏移。
我们使用AssetStudio 来查看assetbundle 里的内容,可以清楚看到资源的分布:




注意:

地形切割之后,隐藏之前的原有地形,放掉TerrainLoadEditor.Load函数里的注释,把所有的地形分片加载到场景进行场景烘焙,这样lightmap记录的所有切割地形的索引和偏移。lightmap生成之后,把对应的贴图打包到对应的assetbundle。运行时,删去所有场景里分片地形,所有的地形都是动态加载的。assetbundle先找到里面的bytes数据,根据数据生成lightmapdata赋值给LightmapSetting,再动态算场景里物件的render所对应的lightmap偏移和索引。设置render的lightmap索引和偏移,会打断unity自身的static batch,为了减轻gpu的渲染负担,在所有设置好偏移和索引之后,可以使用CombineInstance 和MaterialPropertyBlock 等技术进行合批和优化。
github工程里有三个scene:
race_track_lake: 用来测试地形的切割和加载
race_track_lake2:测试lightmap的动态加载(ab)和偏移 不考虑地形切割
race_track_lake3:地形切割且使用lightmap的动态加载
页: [1]
查看完整版本: Unity 大地形加载研究