如何使得Unity Terrain(地形)的高度匹配道路模型的高度

文章目录

      • 0.参考
      • 1.Unity Terrain
      • 2.如何使用代码 get/set Terrain某一点的高度
      • 3.如何获取道路模型的高度,以及需要修改的heightmap区域
      • 4.如何Smooth地形,使得高度变化不那么突兀?

0.参考

  • Unity官方文档:关于Terrain的介绍与API参考
  • Unity Assets Store资源:Road Maker Optimized:这个项目提供了一个API文档,可以查看每个函数的含义,比起自己去看源码寻找,方便很多。此外,由于这个项目不提供源码,所以需要使用ILSpy反编译dll文件。反编译的效果还是不错的,大部分代码是可以看懂的。

1.Unity Terrain

Unity Terrain是什么呢?它在窗口中看起来像一个Mesh,有很多的网格。

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第1张图片
但是它并没有Mesh Filter和Mesh Renderer组件,取而代之的是Terrain组件。

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第2张图片
Mesh好理解,但是这个Texture是什么呢?

Terrain tools that affect height, such as Raise or Lower Terrain and Set Height, use a grayscale texture called a heightmap. Unity represents the height of each point on the Terrain as a value in a rectangular array. It represents this array using a grayscale heightmap. Heightmaps are built into the Terrain, and the values stored in a heightmap define the height of each point or vertex on the Terrain.

根据官方文档的以上描述,我们可以知道,Unity内部是用一个二维数组(rectangular array)来存储Terrain每个点的高度的。而每个点的高度又是从一张灰度图(heightmap)里采样的。

这张灰度图可以在Terrain组件的Texture Resolutions选项卡里Import或者Export,选择导出后我得到了个名为“terrain.raw”的文件。使用PhotoShop打开,可以看到该图片的属性如下:宽度和高度都为513像素,和Terrain组件中的“Heightmap Resolution”的值一致。通道数量为2,深度为8位。

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第3张图片
这张heightmap长这样:

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第4张图片
对比Untiy窗口的Terrain渲染图,我们可以观察到黑色代表高度为0,白色代表高度为最大值(由Mesh Resolution中的Terrain Height定义),灰色则代表(0~maxHeight)中的一个值。

2.如何使用代码 get/set Terrain某一点的高度

(1)heightmap的坐标系

以左下角为原点,向右向上为正。如下图,白色正方体所在的位置为世界坐标系的原点。选中的Terrain在世界坐标系中,坐标和原点重合,position=(0,0,0)。可以看到,虽然移动工具在Terrain的中心,但是Terrain的左下角和position重合。

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第5张图片
(2)将Terrain左下角的五分之一面积的高度设置为0

  • terrain.terrainData.heightmapResolution
  • terrain.terrainData.GetHeights()
  • terrain.terrainData.SetHeights()
public class TestTerrain : MonoBehaviour
{
    Terrain terrain;
    // Start is called before the first frame update
    void Start()
    {
        terrain = GetComponent<Terrain>();
        int resolution = terrain.terrainData.heightmapResolution;
        //GetHeights函数:获取从(xbase,ybase)开始的resolution的点的height。
        //即hts存储了heightmap上所有点的高度值。
        float[,] hts = terrain.terrainData.GetHeights(0, 0, resolution, resolution);
        for(int i = 0; i < resolution / 5; i++)
        {
            for(int j = 0; j < resolution / 5; j++)
            {
                hts[i,j] = 0;
            }
        }
        terrain.terrainData.SetHeights(0, 0, hts);
    }
}

效果如图:

修改前 修改后
如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第6张图片 如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第7张图片

需要注意的是,即使是在运行时修改了Terrain的height,效果也是永久的,因为这里修改的实际上heightmap,图像已经改变。

3.如何获取道路模型的高度,以及需要修改的heightmap区域

答案就是使用Raycast!我们可以从heightmap上的每一个点发射一条射线,如果这条射线和road模型相交了,就可以得到交点处的坐标值。我们就可以将heightmap上的该点的高度设置为交点的y值了。

public class TestTerrain : MonoBehaviour
{
    Terrain terrain;
    // Start is called before the first frame update
    void Start()
    {
        //【Terrain Data】
        terrain = GetComponent<Terrain>();
        int resolution = terrain.terrainData.heightmapResolution;
        Debug.Log("resolution:" + resolution);
        float sizeX = terrain.terrainData.size.x;
        float sizeY = terrain.terrainData.size.y;
        float sizeZ = terrain.terrainData.size.z;
        Debug.Log("size x:" + sizeX+" size y:"+sizeY + " size z:" + sizeZ);
        float posX = terrain.transform.position.x;
        float posY = terrain.transform.position.y;
        float posZ = terrain.transform.position.z;

        //【Process】
        //获取从(xbase,ybase)开始的resolution的点的height。即hts存储了heightmap上所有点的高度值。
        float[,] hts = terrain.terrainData.GetHeights(0, 0, resolution, resolution);
        RaycastHit hit;
        for (int y = 0; y < resolution; y++)
        {
            for (int x = 0; x < resolution; x++) { 
                Vector3 origin = new Vector3(
                    (float)x / (float)(resolution - 1) * sizeX + posX,
                    posY+terrain.terrainData.GetHeight(x,y),
                    (float)y/(float)(resolution-1)*sizeZ+posZ
                );
                if(Physics.Raycast(new Vector3(origin.x,origin.y-sizeY,origin.z), Vector3.up, out hit, sizeY * 2)){
                    hts[y, x] = (hit.point.y - posY) / sizeY;
                }
            }
        }
        terrain.terrainData.SetHeights(0, 0, hts);
    }

效果如图:

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第8张图片

4.如何Smooth地形,使得高度变化不那么突兀?

研究ing……

如何使得Unity Terrain(地形)的高度匹配道路模型的高度_第9张图片

你可能感兴趣的:(自动驾驶仿真)