前段时间有一个小需求,模拟汽车烟尘烟尘尾迹仿真的算法,其中有一部分是需要判断汽车行驶的地形材质,根据地形材质来实现不同的效果,感觉这个功能还不错,于是拿出来给大家分享与交流。
其中借鉴了一个大佬的方法:http://answers.unity3d.com/questions/34328/terrain-with-multiple-splat-textures-how-can-i-det.html;
因为公路马路等一些地形是用的模型,于是最开始我的思路是在汽车轮胎下面发出射线,将模型设定标签,来进行检测。但是由于还有地形(Terrain)刷的草地、沙漠等地形通过射线无法判断,冥思苦想之后,终于在网上找到了一个很好的方法。
public Terrain terrain; // 定义一个指定地形
//在当前的地形上返回一个指定点的相对混合的纹理的一个数组
//返回的数组中的长度将等于已经添加到地形的材质数组
//worldPos 目标的坐标,在项目里指汽车坐标
public float[] GetTextureMix(Vector3 worldPos)
{
TerrainData terrainData = terrain.terrainData;
Vector3 terrainPos = terrain.transform.position;
//Debug.Log(terrainData);
//计算指定的worldpos的网格贴图细节(忽略Y值)
int mapX = (int)(((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
int mapZ = (int)(((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);
//Debug.Log("mapX :" + mapX + " maxZ:" + mapZ);
//当运动物体的坐标超出地形时返回null
if (mapZ < 0 || mapZ > terrainData.size.z || mapX < 0||mapX>terrainData.size.x)
{
return null;
}
//以1x1xN 3d数组的形式获取此单元格的splat数据 (其中N =纹理数量)
float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 1, 1);
//Debug.Log("splatmapData+ "+splatmapData[0,0,0].ToString());
// 将三维数组数据提取到一维数组中:
float[] cellMix = new float[splatmapData.GetUpperBound(2) + 1];
for (int n = 0; n < cellMix.Length; ++n)
{
cellMix[n] = splatmapData[0, 0, n];
}
return cellMix;
}
此方法用来返回汽车在地形上(忽略Y轴)的一个材质数组。(Terrain在此位置上可能出现的一个或者多个材质混合的数组)。
//此方法用来返回在上述方法中获取的材质数组中权重最大的索引
public int GetMainTexture(Vector3 worldPos)
{
float[] mix = GetTextureMix(worldPos);
//运动目标超出地形范围
if (mix==null)
{
//错误
return 100001;
}
// 返回最主要纹理的从零开始的索引
// 在这个地形上。
float maxMix = 0;
int maxIndex = 0;
// 循环遍历每个混合值并找到最大值
//循环找到最大的那个就是当前贴图的index
for (int n = 0; n < mix.Length; ++n)
{
if (mix[n] > maxMix)
{
maxIndex = n;
maxMix = mix[n];
}
}
return maxIndex;
}
此时已经获取到了当先运动物体(汽车)所处地形的材质索引。接下来我们来获取地形中材质的名称。
//获取地形材质的名称
public string GetTextureName (Vector3 WorldPos) {
TerrainData td = terrain.terrainData;
//获取地形使用的样条纹理
SplatPrototype[] sp = td.splatPrototypes;
//当前地形材质贴图数量为0,返回null
if(sp.Length==0)
{
return null;
}
//获取地形中材质混合最高的材质索引
int index = GetMainTexture(transform.position);
//发生运动物体(汽车)超出地形范围。
if (index==100001)
{
Debug.Log("100001");
return "100001";
}
return sp[index].texture.name;
}
此时我们已经拿到运动物体(汽车)在地形上的材质名称,通过材质名称来设置我们想要的效果。