简介
在看了狗哥老司机和MaxwellGeng等大佬关于GPUDriven的实现,就参考Ubisoft实现一个简单的GPUDriven的Terrain的绘制
因为地形绘制时需要的数据不需要一直更新,所以只要在一开始传入地形的HeightMap和NormalMap之后,剩下的绘制操作基本不太需要传入其他大量的数据,非常适合GPUDriven。
使用GPUDriven的好处是可以在很少的几次DrawCall就可以绘制出整个地形场景,而且可以剔除掉不需要的面片,减少绘制的压力。
做GPUDrivenTerrain需要注意
- Hiz的生成,地表mesh的拆分与剔除
- 不同mip的mesh的临接的接缝处理
- 在GPU上实现的地形数据结构,以及灯光的处理(我没有去实现)
实现流程
- 使用一个64*64大小的mesh作为Instance的对象,可以通过四叉树把当前的地形切分,分成3级
- 使用上一帧的depth计算出的Hiz和上一帧的ViewProjection的Matrix对当前帧的做一次Cull
- 用第2步剪裁的结果绘制出深度,并生成当前深度的Hiz
- 通过第3步生成的Hiz和当前帧的ViewProjection的Matrix再次对第2步剪裁后剩下的部分再做一次Cull
- 对第4步得到的新的深度重新计算Hiz,作为本帧最终的Hiz,同时也作为下一帧的输入Hiz
- 通过剔除操作得到需要绘制的mesh的ID和
我们在Unity上实现的时候,场景跟地形分开绘制,具体实现代码如下
// 0、绘制得到Opaque的深度图
m_DepthPrepass.Setup(cameraTargetDescriptor, new RenderTargetIdentifier(m_DepthRenderTarget));
EnqueuePass(m_DepthPrepass);
// 1、根据上一帧的Hiz和VP先剔除掉有可能被遮挡的地块,在Opaque的深度图上继续绘制深度
m_TerrainDepthPrepass.Setup(new RenderTargetIdentifier(m_DepthRenderTarget), m_HizRenderTarget, _VPPrevFrame);
EnqueuePass(m_TerrainDepthPrepass);
// 2、使用新的depth计算Hiz
m_HizPass.Setup(new RenderTargetIdentifier(m_DepthRenderTarget), m_HizRenderTarget);
EnqueuePass(m_HizPass);
// 3、使用新的Hiz做剔除,绘制剩下的其实还存在的小块
m_TerrainDepthPrepass.Setup(new RenderTargetIdentifier(m_DepthRenderTarget), m_HizRenderTarget, _VPPrevFrame);
EnqueuePass(m_TerrainDepthPrepass);
// 4、计算当前帧的depth计算Hiz,以便下一帧使用
m_HizPass.Setup(new RenderTargetIdentifier(m_DepthRenderTarget), m_HizRenderTarget);
EnqueuePass(m_HizPass);
// 正常绘制场景
EnqueuePass(m_RenderOpaqueForwardPass);
// 正常绘制地形
m_GPUTerrainPass.Setup(BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget);
EnqueuePass(m_GPUTerrainPass);
对于绘制阴影也是跟上面同样的剔除方法,只不过光源作为相机的位置和方向而已,跟正常的shadowmap绘制没有太大区别。
接缝处理
参考狗哥老司机和Ubisoft介绍的,通过对mesh进行退化,可以防止不同mip等级的mesh之间相连接的时候会出现缝隙,通过把在小mip的一个点移动到已知的点的位置上,而这个移动的距离就存在mesh的color属性中。
使用的mesh是直接狗哥老司机的项目中的在顶点的color上设置了偏移值的mesh,右边是存到了alpha通道上,所以看不见
在实际绘制的时候,如下图所示,不同mip的mesh连接到一块儿的时候如果不做偏移的处理是这样的,小的mip会有一个顶点在大的mip边上,这样在根据heightmap对顶点的位置做偏移的时候,就会导致该边上多出来的这个顶点凸出去,而且产生缝隙
处理的方法就是提前在在不同mip临接的边上,对低mip的mesh多出来的这个顶点做退化处理,这点跟Ubisoft提到的用greedy的方法预填充场景对象的mesh方法一样,要对mesh重合或者不同mip临接的顶点做退化处理,其实就是把这个顶点偏移一定的距离,使其与mesh内部的顶点重合。
做了顶点偏移处理后的mesh如上图所示,不同mip的mesh相接的地方就不会出现接缝
Unity版本的实现请看 https://gitee.com/alienity/GPUDrivenTerrain
引用
[1] https://zhuanlan.zhihu.com/p/335325149
[2] https://zhuanlan.zhihu.com/p/352850047
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/26447.html