一:获取terrain信息
1.首先要看我们要组成多大的尺寸的地形碎片,比如32x32的或64x64的。就从terrain中获得指定尺寸大小的信息。
2.然后这里需要建立四个mipmap,从一个四边形的四个边继续细化三角形。四个对象就是他的子节点。也就时如果我们确定32x32的话,mip就是16x16,8x8,4x4
二:根据摄像机距离获取具体mesh
1.根据摄像机与node的距离,以100为分割距离选择不同的mipmap,比如距离在100以内用mip为0的,在800以外的用mip为3的。因为用的是100 * Mathf.Pow(2, mip)
2.这里还会保存node上下左右四个邻居的信息,保存的信息是看上下左右用的mip是不是比自己大,如果比自己打则要做一些接缝处理。
三:根据terrain组装法线信息
1.这里的组装方式就是获取terrain的高度图,然后通过terrain的法线信息接口获取高度图范围内的发现信息,并写入颜色里面组成一张图片,注意高度图的信息在x内,而我们的法线信息在z内。所以需要把高度图的x赋值到normal的z。
四:根据深度图创建四级mipmap
1.另外我们需要建立深度图的,这里是用一个rt然后拿到渲染管线给的depthtexture然后分几个pass去不断减图片大小。
然后在computeshader中执行mip的图像信息是找四个旁边的像素中最小的一个值为这个mip的像素值。
五:computeshader剔除不可见信息
1.首先拿到当前node的高度信息的最高位置和最低位置。
· 2.然后根据网格的8个边缘顶点,转换到屏幕坐标然后判断是否xyz都在范围内,看绝对值是否在-1到1的范围内。
转存失败重新上传取消
需要注意的是这里拿了深度图的信息来做剔除,就会让最后可见的node只会是深度信息内的,倍遮挡的就会不见。
六:渲染地形
1.这里的话需要用到gpuinstance,通过DrawMeshInstancedIndirect的方式传入我们指定的mesh和我们要创建的mesh的数量,用指定的渲染的shader执行。其中bufferWithArgs就是确定需要多少个实例化多少个mesh。
2.shader中我们要获取到具体的node信息,包括位置和长款,以及他的邻居上下左右的mip信息是否大于自己。
3.然后我们要计算顶点的世界坐标,因为rect本身就是世界坐标了,所以要用rect的xy作为起始坐标,然后因为网格的每个顶点的位置不一样,所以需要本地坐标,然后乘上他的长宽的0.25,跟上面一样,因为长宽各有四个顶点,所以是1/4的位置。然后diff是偏移,也就是处理接缝的信息,根据邻居是上下左右哪一边来确定用mesh里的哪个通道,这里的通道的偏移已经提前写入到mesh中才行。用四个通道即可表示上下左右的偏移。
4.还得注意他的高度,不然这个node下的mesh就是扁平的
高度信息是拿高度图和整个terrain的高度相乘再乘2得到的,乘以2和前面的size只做用于一半。
七:渲染阴影
1.渲染阴影需要在渲染管线在渲染阴影的阶段加上我们的渲染方法,在urp就是在shadowutils中RenderShadowSlice时开始计算提出和渲染阴影,
2.剔除还是在compute中剔除
但是他的边缘顶点需要做一些偏移
剔除的方式是首先往光照方向偏移深度bia个值。然后往取光照方向与法线方向点乘的反方向与法线偏移相乘后的值为法线偏移。
3.然后shader渲染时的世界坐标xyz还是和网格渲染时一摸一样。
八:通过rendererfeature整合
因为用的时urp,所以需要feature整合一下
九:总结
这种方式可以大量减少网格的数量,因为terrain是会非常密的网格,而且不会剔除被遮挡的信息,所以我们这样做可以减少非常多的网格。
这个是用gputerrain的:
这个是原始的terrain
另外terrain增加了那么多的dc是因为我没有开shadow然后他有shadow,另外就是听说他会拿多6个pass来整理接缝问题。
而且我们可以自己组织地表的渲染,所以可以更多的控制性能消耗。
但是他用了computeshader和gpuinstance。所以得opengl es 3.1以上才能支持。虽然说gpuinstance可以在3.0执行,但部分手机也得在3.1支持。所以总的来说还是要在3.1支持下用才比较可靠。