动态加载烘培贴图与Terrain转mesh
http://blog.csdn.net/zr339361504/article/details/53352800
前言
unity加载烘培贴图是需要加载场景才可以使用,但如果项目只使用一个场景或者有许多关卡地形时,明显通过加载场景来达到更换烘培贴图的这种做法是不妥当的。而terrain地形在有些安卓机上的支持并不是很好,所以有必要把地形转为网格。庆幸的是,网上也有这方面的代码,所以借鉴了网上大神的代码,整合出一套动态加载烘培贴图与地形转网格的方案。
借鉴的网站:
http://www.xuanyusong.com/archives/3807
http://www.cnblogs.com/jietian331/p/5831062.html
一、动态加载烘培贴图。
1、烘培贴图的原理
烘培贴图实际上就是把静态物体上的阴影与光照保存成多张png贴图,并保存每个静态物体对应烘培贴图的编号、裁剪大小与裁剪偏移量。最后,静态物体与关联的裁剪纹理在shader里进行处理。
2、有用的数据
我们知道烘培以后会生成Lightmap Data Asset(5.3之前叫LightmapSnapshot)、贴图、与SkyboxProbe。实际上真正运行的时候只需要贴图以及上面说的每个静态物体对应烘培贴图的编号、裁剪大小与裁剪偏移量。所以,我们只需要把裁剪信息保存起来就可以了。
3、数据保存
我们可以使用文件来保存这些数据,但是有一种更方便的做法,也是本文用到的方法。就是通过挂载脚本,把数据存放在脚本中,在做成预置。这样的做法,就可以把数据完完整整的保存在预置里。
二、Terrain转Mesh
1、实现原理
首先我们要明白,要生成网格需要顶点、三角面,如果有贴图光照还需要uv和发现。明白了这点,我们就可以从这几个方向下手。只要获取到地图的宽高作为行与列,用行与列对应的点作为x与z坐标,再根据此点获取地形高度作为z坐标,就可以生成顶点,再根据顶点来连接三角面。uv坐标也类似,只是把范围缩放到0与1。
2、有用的数据
生成地形网格,我们需要保存顶点数据、瓦片贴图权重、瓦片uv坐标、光照贴图uv坐标、三角面、以及。由于与地形的光照部分直接使用烘培贴图,所以法线可以不用记录。
3、数据保存
之前曾视图使用.obj格式来保存网格数据,但是查看了一下.obj的数据格式,发现其只支持一套uv坐标,不支持多套。所以只能用上面提到的使用脚本预置来保存数据。
三、使用方法
创建一个空物体,添加PrefabLightmapData脚本,并把所有地形相关物体都放在空物体上,成为其子节点。点击Bake/Bake Prefab Lightmaps便可以。生成以后,需要运行一次才会设置材质。有个地方需要注意,由于项目出于效率的考虑,Directional Mode需要设置为Non-Directional。
四、代码
1、PrefabLightmapData.cs
#if UNITY_EDITOR
using UnityEditor;
using System.IO;
#endif
using UnityEngine;
using System.Collections.Generic;
[DisallowMultipleComponent, ExecuteInEditMode]
public class PrefabLightmapData : MonoBehaviour
{
[System.Serializable]
struct RendererInfo
{
public Renderer renderer;
public int lightmapIndex;
public Vector4 lightmapOffsetScale;
}
[System.Serializable]
struct TerrainInfo
{
public int lightmapIndex;
public Vector4 lightmapOffsetScale;
}
[System.Serializable]
public struct FogInfo
{
public bool fog;
public FogMode fogMode;
public Color fogColor;
public float fogStartDistance;
public float fogEndDistance;
public float fogDensity;
}
[SerializeField]
TerrainInfo[] m_TerrainInfo;
[SerializeField]
RendererInfo[] m_RendererInfo;
[SerializeField]
Texture2D[] m_Lightmaps;
[SerializeField]
Texture2D[] m_Lightmaps2;
[SerializeField]
FogInfo m_FogInfo;
const string LIGHTMAP_RESOURCE_PATH = "Assets/Resources/Lightmaps/";
[System.Serializable]
struct Texture2D_Remap
{
public int originalLightmapIndex;
public Texture2D originalLightmap;
public Texture2D lightmap0;
public Texture2D lightmap1;
}
static List<Texture2D_Remap> sceneLightmaps = new List<Texture2D_Remap>();
void Awake()
{
ApplyLightmaps(m_RendererInfo, m_TerrainInfo, m_Lightmaps, m_Lightmaps2,m_FogInfo);
for (int i = 0; i < m_TerrainInfo.Length; i++)
{
var info = m_TerrainInfo[i];
TerrainToMeshData terrianMeshData = GetComponentInChildren<TerrainToMeshData>();
if (terrianMeshData)
terrianMeshData.setMaterial(info.lightmapIndex);
}
}
static void ApplyLightmaps(RendererInfo[] rendererInfo, TerrainInfo[] terrainInfo, Texture2D[] lightmaps, Texture2D[] lightmaps2, FogInfo fogInfo)
{
for (int i = 0; i < rendererInfo.Length; i++)
{
var info = rendererInfo[i];
info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
info.renderer.lightmapIndex = info.lightmapIndex;
}
LightmapData[] combinedLightmaps2 = new LightmapData[lightmaps.Length];
for (int i = 0; i < lightmaps.Length; i++)
{
combinedLightmaps2[i] = new LightmapData();
combinedLightmaps2[i].lightmapFar = lightmaps[i];
combinedLightmaps2[i].lightmapNear = lightmaps2[i];
}
LightmapSettings.lightmaps = combinedLightmaps2;
RenderSettings.fog = fogInfo.fog;
RenderSettings.fogMode = fogInfo.fogMode;
RenderSettings.fogColor = fogInfo.fogColor;
RenderSettings.fogStartDistance = fogInfo.fogStartDistance;
RenderSettings.fogEndDistance = fogInfo.fogEndDistance;
RenderSettings.fogDensity = fogInfo.fogDensity;
LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
//bool existsAlready = false;
//int counter = 0;
//int[] lightmapArrayOffsetIndex;
//if (rendererInfo == null || rendererInfo.Length == 0)
// return;
//LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
//var settingslightmaps = LightmapSettings.lightmaps;
//var combinedLightmaps = new List<LightmapData>();
//lightmapArrayOffsetIndex = new int[lightmaps.Length];
//for (int i = 0; i < lightmaps.Length; i++)
//{
// existsAlready = false;
// for (int j = 0; j < settingslightmaps.Length; j++)
// {
// if (lightmaps[i] == settingslightmaps[j].lightmapFar)
// {
// lightmapArrayOffsetIndex[i] = j;
// existsAlready = true;
// }
// }
// if (!existsAlready)
// {
// lightmapArrayOffsetIndex[i] = counter + settingslightmaps.Length;
// var newLightmapData = new LightmapData();
// newLightmapData.lightmapFar = lightmaps[i];
// newLightmapData.lightmapNear = lightmaps2[i];
// combinedLightmaps.Add(newLightmapData);
// ++counter;
// }
//}
//var combinedLightmaps2 = new LightmapData[settingslightmaps.Length + counter];
//settingslightmaps.CopyTo(combinedLightmaps2, 0);
//if (counter > 0)
//{
// for (int i = 0; i < combinedLightmaps.Count; i++)
// {
// combinedLightmaps2[i + settingslightmaps.Length] = new LightmapData();
// combinedLightmaps2[i + settingslightmaps.Length].lightmapFar = combinedLightmaps[i].lightmapFar;
// combinedLightmaps2[i + settingslightmaps.Length].lightmapNear = combinedLightmaps[i].lightmapNear;
// }
//}
//ApplyRendererInfo(rendererInfo, lightmapArrayOffsetIndex);
//LightmapSettings.lightmaps = combinedLightmaps2;
}
static void ApplyRendererInfo(RendererInfo[] infos, int[] arrayOffsetIndex)
{
for (int i = 0; i < infos.Length; i++)
{
var info = infos[i];
info.renderer.lightmapIndex = arrayOffsetIndex[info.lightmapIndex];
info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
}
}
#if UNITY_EDITOR
[MenuItem("Bake/Update Scene with Prefab Lightmaps")]
static void UpdateLightmaps()
{
PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
foreach (var instance in prefabs)
{
ApplyLightmaps(instance.m_RendererInfo, instance.m_TerrainInfo, instance.m_Lightmaps, instance.m_Lightmaps2, instance.m_FogInfo);
}
Debug.Log("Prefab lightmaps updated");
}
[MenuItem("Bake/Bake Prefab Lightmaps")]
static void GenerateLightmapInfo()
{
Debug.ClearDeveloperConsole();
if (Lightmapping.giWorkflowMode != Lightmapping.GIWorkflowMode.OnDemand)
{
Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
return;
}
Lightmapping.Bake();
string lightMapPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), LIGHTMAP_RESOURCE_PATH);
if (!Directory.Exists(lightMapPath))
Directory.CreateDirectory(lightMapPath);
sceneLightmaps = new List<Texture2D_Remap>();
//var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
var sceneName = Path.GetFileNameWithoutExtension(EditorApplication.currentScene);
var resourcePath = LIGHTMAP_RESOURCE_PATH + sceneName;
var scenePath = System.IO.Path.GetDirectoryName(EditorApplication.currentScene) + "/" + sceneName + "/";
PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();
foreach (var instance in prefabs)
{
var gameObject = instance.gameObject;
var rendererInfos = new List<RendererInfo>();
var terrainInfo = new List<TerrainInfo>();
var lightmaps = new List<Texture2D>();
var lightmaps2 = new List<Texture2D>();
GenerateLightmapInfo(scenePath, resourcePath, gameObject, rendererInfos, terrainInfo, lightmaps, lightmaps2);
instance.m_RendererInfo = rendererInfos.ToArray();
instance.m_Lightmaps = lightmaps.ToArray();
instance.m_Lightmaps2 = lightmaps2.ToArray();
instance.m_TerrainInfo = terrainInfo.ToArray();
instance.m_FogInfo = new FogInfo();
instance.m_FogInfo.fog = RenderSettings.fog;
instance.m_FogInfo.fogMode = RenderSettings.fogMode;
instance.m_FogInfo.fogColor = RenderSettings.fogColor;
instance.m_FogInfo.fogStartDistance = RenderSettings.fogStartDistance;
instance.m_FogInfo.fogEndDistance = RenderSettings