unity识别地形材质

前段时间有一个小需求,模拟汽车烟尘烟尘尾迹仿真的算法,其中有一部分是需要判断汽车行驶的地形材质,根据地形材质来实现不同的效果,感觉这个功能还不错,于是拿出来给大家分享与交流。

其中借鉴了一个大佬的方法: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;
    }

此时我们已经拿到运动物体(汽车)在地形上的材质名称,通过材质名称来设置我们想要的效果。

总结:第一步先通过射线检测Collider的方式来进行第一层筛选(铺在地形上方的公路等材质),第二步进行地形材质的判断,通过这两步,基本能满足运动物体在地形上的判断。

猜你喜欢

转载自blog.csdn.net/qq_40097668/article/details/90177205