| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 游戏开发 -> UE4 Hi-Z遮挡剔除实现详细解析 -> 正文阅读 |
|
[游戏开发]UE4 Hi-Z遮挡剔除实现详细解析 |
Hi-Z RenderTarget的生成Pixel Shader处理Hi-Z RenderTarget情况: InvSize:输入上一级(高等级)HiZ图的MinMap大小,(比如输入是512的MinMap0,输出是256的MinMap图)。 InputViewportMaxBound:采样的贴图UV最大边界,min作用防止采样超出贴图界限。 获取到上一级Minmap的4个深度后将最小的那个深度返回。 这里为什么不是0.5而是0.25,看下图就能够一目了然了。 float4 DeviceZ = Gather4(ParentTextureMip, ParentTextureMipSampler, BufferUV); float FurthestDeviceZ = min(min(DeviceZ.x, DeviceZ.y), min(DeviceZ.z, DeviceZ.w)); 图一计算出4个像素的深度,然后将最小深度输出到下一级MinMap。 Primitive裁剪信息存到GPU将Primitive裁剪包围盒写到两张RT上: bAllowBoundTest:表示在玩家视锥有效范围内的PrimitiveOcclusion LastTestFrameNumber:记录Primitive在哪一帧被加入计算裁剪 HZBTestIndex:在这里调用AddBounds将裁剪包围盒添加到队列中,并返回当前自己在队列中的Index。 队列是顺序队列,按照顺序依次加入 FHZBOcclusionTester::Submit这里将会把所有Primitive的裁剪包围盒分别写到两张RT上(Center和Extent)。 填充方式: Size:整张贴图的大小,目前ue给的256*256。 Block:大小是8*8,下图中绿色方块。 Cpu上会把裁剪包围盒的信息以Block*Block作为一块进行存储,并依次从贴图左上角从左往右填充,满了会换行继续填充,直到所有裁剪包围盒被填充完为止。 PS上裁剪测试: 首先根据根据Center和Extent两个值算出包围盒8个顶点的位置,然后将每个点转换到屏幕空间中,在将它们中3个维度的最小值和最大值存储到RectMin和RecMax中,XY用于后续计算MinMap使用,Z用于裁剪测试。 ·根据RectMin和RectMax构造出在屏幕空间的矩形,因为NDC下的点是-1~1之间,所以乘0.5+0.5转到0~1之间。 ·Hi-Z图Size的XY存储的是Hi-Z图这张图的分辨率,ZW存储的是1/HZB这张图的分辨率,因此RectPixels算出矩形在Hi-Z图这张图上所在的位置。 ·然后通过最大减最小算出两条边的长度RectSize。 ·最后通过拿到最长的那条边做Log2计算算出要使用哪一集MinMap。 HZBUvFactor的XY是当前Pass输出的宽高大小与HZB图大小的比值,所以Scale是图与Pass输出的适配,除3没看明白是为了什么,我猜应该是测试得出不会出现裁剪错误的个值。 然后通过采取HZB图在当前像素中周围16个像素,并获取最小深度那个值,最后与RectMax的做对比,如果裁剪包围盒最大深度小于HZB的16个像素中的最小深度,那么被遮挡 在下一帧的头部会把数据进行回读到Resultsbuffer。 接下来就是遮挡查询,前面提到的在AddBound的时候把LastTestFrameNumber保存了,这里利用这个值测试该Primitive是否在上一帧有参加遮挡剔除计算,然后将Index传入查询。 IsVisble里的查询其实就是将Index根据上面将裁剪包围盒存储到图中的方式逆向得出索引来索引ResultBuffer,最终获得遮挡结果。 移动端实现:移动端最大的问题是回读、和设备兼容性的问题,这里不讨论兼容问题。 由于UE4的RHI线程往往会滞后Render线程一个InitView(4.27版本),如下图,第2帧的RHI在第3帧的Render线程InitView FlushRHI前才完成所有DrawCall提交,如果没完成Render线程会等RHI提交完成,所以理论上会在下一帧前保证指令发送到GPU上,如果是回读指令,这帧未必能读回,因为GPU有可能没有执行完,然后就要再等一帧,所以可能要过2帧才能读回。 保守起见回读延迟3帧。 创建3个RWBuffer:创建可读写的Buffer。 创建3个GPUBufferReadback:用于将RWbuffer中的数据回读到CPU。 为什么需要创建UAV类型的RWBuffer,因为移动端不支持RT的回读,但通过Computer可以操作UAV的Buffer回读。 然后在FHZBOcclusionTester::Submit函数中根据平台分一下回读的方式。 ·首先% 3求余获取需要读到哪个Buffer上 ·然后将GPU裁剪测试输出的结果RT,通过Computer Shader输出到RWBuffer,由于当前数据还是在GPU,所以使用GPUBufferReadback回读到CPU,仔细跟踪到opengl底层会发现回读方式使用的是PBO(Pixel Buffer Object)。 PBO细节:OpenGL深入探索——像素缓冲区对象 (PBO)_ShaderJoy的博客-CSDN博客_gl_pixel_pack_buffer 将回读的结果拷贝出来到数组中,PC平台调用的是void FHZBOcclusionTester::MapResults,这里需要改成如下,根据Buffer的大小将裁剪结果数据读回到,Buffer的大小是贴图的内存大小。 这里需要注意的是这里数据的获取是在Render线程的下一帧,也就是结果是Render线程提交的。 竟然是上一帧的为什么这里是ValidFrameNumber – 2而不是减3,如下图,在开启HZB遮挡剔除的情况下,ValidFrameNumber的递增是在ReadBackResults/MapResults之后的。 移动端实现细节就这样。 问题:在Meta(ios)机器上,如果你需要GPU跑60fps,当帧率低于60fps的时候回读的数据会有错乱,目前还未找出原因。 Hi-Z遮挡剔除学习后的分享,如有什么错误欢迎在评论区提出。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 | -2025/1/17 5:58:33- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |