育碧作为一个年货大厂,在PCG这方面肯定有很深的积累,在GDC2018的这次分享中,其详细的介绍了地形在纯GPU管线上的实现方式,使得最终能够得到一个拥有大片悬崖峭壁的真实地形。
本文是笔者看完FarCry5的分享后写的个人理解,初次学习GPU管线,难免有理解错误的地方,希望大佬指正
本次分享依次介绍了以下内容
1.GPU Pipeline实现
2.地形渲染
3.悬崖渲染
4.高度图之外的技术
5.基于屏幕空间的渲染
6.其他地形相关
GPU Pipeline实现
首先说一下单个平面的实现
先渲染一个毫无起伏的纯平面Mesh
在顶点着色器中,根据InstanceID和vertexId可以得到在高度图上的对应位置,依据此位置去对顶点进行偏移
配合贴图可得到以上效果
之后介绍了如何将四叉树的分化逻辑移到GPU,四叉树的CPU实现已经介绍过了,这里不再细讲
https://zhuanlan.zhihu.com/p/88646346
按2km*2km对地形划分为多个树结构
分化到最大的地形块边长为64米,被称为一个Sector,总共有160*160个sector,也就是如果所有树分化到最大,会存在160*160个lod最大的地形块
正常游戏中会加载大概500个地形块(包含任意lod等级)
正常游戏中,地形一共存在6个lod等级
每一个树结构都对应一个mipmap,mipmap的层级和lod层级相同
每一个Node的信息都保存在对应Mip等级mipmap的一个像素中
像素值是一个16位的Index,根据这个index,可以去Buffer,也就是一个数组中去拿到节点的详细信息,包括高度范围,lod信息,贴图位置信息
如果这个节点是不存在的(地形不是一个标准正方形),则会把一个特殊值赋给index
四叉树是一个递归的结构,但递归难以在GPU上实现,所以这里采用该算法的循环形式
一共存在3个存储节点信息的buffer
首先把根节点放入bufferA,遍历其中每个节点
如果不可分化,则将该节点加入FinalBuffer
如果可分化,则将子节点放入bufferB
当根节点分化完毕,会互换AB两个buffer的作用,清空BufferA,然后遍历bufferB,将子节点加入bufferA。直到处理完所有lod
同时也会将每个lod的节点有多少记录下来
这里还说了一个优化,但笔者没用过computershader,所以也没看懂~
同时会生成一个LODMap,每个Sector都会被赋予一个LOD值,用于解决裂缝问题。
LOD的值能够根据FinalBuffer轻松获取
每一个Node会被分为64个Patch,Patch即可视为一个具体的vertex
为16*16的Mesh,经过一系列cull,最终把可视的Patch放到一个RenderLst.
遮挡剔除采用的Hiz,主机上会采用纯Gpu实现,Hiz的算法知乎上有很多文章,之后有时间我也会按自己的理解写一下。
而PC则会把CPU上产生的深度图上传到GPU(这里我的理解是cpu上有剔除逻辑,会把中间生成的深度图传至gpu进一步剔除,但是ppt没讲为什么这么搞)
On console platforms we use a GPU best occluder pass to prime the depth buffer and extracttheHTile.
On PC we have a software rasterized occlusion buffer that we already use for CPU visibility which we upload to atexture.
背面剔除,针对每一个Patch,会离线为其生成一个圆锥体,这个圆锥体Patch内所有顶点法线组成的最小圆锥。之后会把信息存到一个Tex中。
运行时直接如上计算可以判断是否需要剔除
每一个patch都会存储它周围节点的lod信息,如上图,当前节点lod为3,通过周围节点的lod可以得到一个值。根据此值对顶点进行干预以解决裂缝问题
地洞渲染的思路是地形上开两个洞,中间的管道采用单独的mesh渲染
这里用到了一个小技巧,如果是洞穴口,则让坐标除0得到NaN,如果一个顶点坐标为NaN,则用到此顶点的面片都会被discard掉。
PPT下载:
链接:https://pan.baidu.com/s/1nRoaaoYI7ln0wyo6Sqppiw
提取码:4jly
相关阅读:
全境封锁UI设计指南
作者:风和雨林
专栏地址:https://zhuanlan.zhihu.com/p/88859361