当前位置: 代码迷 >> 综合 >> GPU Gems 2 三:几何实例化的原理 四:分段缓冲 五:多流管理 六:遮挡查询
  详细解决方案

GPU Gems 2 三:几何实例化的原理 四:分段缓冲 五:多流管理 六:遮挡查询

热度:99   发布时间:2023-11-21 16:08:05.0

3.1 实例化是什么?为什么要实例化?

实例化就是生成一个个体,比如一棵树,一簇草丛,实例化的目的是为了节省性能,因为每一棵树我们不能调用一次绘制API,我们需要把尽可能多的东西,合批,去渲染,实例化就是我们把一个东西生成多份,比如很多树,一次绘制。

3.2 定义

几何体:游戏物体就像类和子类的关系,每一个实例基本体有共同的属性,比如顶点缓存,实例类型,骨骼等,而具体到每一棵树又都有不同的属性,比如树叶的颜色,树叶的数量。几何体就是子类。

一个批次中要渲染很多个几何体。

3.3 实现

渲染实现包含四种方法:静态合批、动态合批、顶点常量实例化、调用几何体实例化的API

静态批次:数据一次性让GPU绘制出来,不支持蒙皮(骨骼),灵活性低,但是简单,需要一次性分配内存,即:顶点缓存和索引缓存,定义完就不可以更改了。

动态合批:动态生成几何体,支持蒙皮,但是耗费时间,也需要预先定义存储空间。

顶点常量实例化:把一个几何体复制多份,传递给GPU,然后在顶点着色器中,根据顶点常量来制 造几何体实例的差异性。 限制就是:每一个批次设置的顶点常量有限制。

调用几何体绘制的API:限制:只能批次渲染同一个几何体包的实例,但是节省了内存消耗

四:分段缓冲

4.1 问题

如果一个场景中有很多个相同几何体包的不同实例,虽然我们可以在同一个批次中渲染它们,但是这需要我们每个实例都进行差异化处理,比如旋转矩阵等,也可以让美工在做的时候,把相邻的模型合并成单个模型,但是这样增加了美工的工作量。

4.2 分段缓冲

分段缓冲会自动合并相似的实例,同时保留了渲染独立实例的能力。

原理就是为每个实例分组,形成一个链表,然后分段渲染。

五:用多流来优化资源管理

当一个场景中有多的顶点数据需要渲染,则有的GPU能渲染,有的GPU 不能渲染,我们需要根据不同的GPU 调整我们的数据内容,多流:就是根据不同的情况,采取不同的结构体,不同的结构体里面的内容不一样,比如用于顶点渲染的基本结构体,包含顶点位置,法线位置等

如果和当前的性能不匹配,就用最基本的结构体,传递最基本的数据。

资源管理:

每一个资源都有一个GUID,用来全局搜索该资源,同时用寄存器来存储网格数据,让GPU 直接和显存交互,而不用从内存里读取。

六:遮挡查询

前提:

遮挡查询的目的是找到哪些视野中看不到的物体,从而从绘制的数据里面剔除它。

最简单的方法就是:

对屏幕空间的像素进行深度排序,也就是z-test,但是这样有两个缺点,一个是要知道一个像素绘不绘制需要等待查询的结果,这十分耗费时间,第二个缺点是复杂的场景需要遮挡查询的物体非常多。

6.4 层

6.4.1 为什么使用层?

我们把场景中的物体分层,也就是上面说的栅格单元,可以使用k-d 树,八叉树 等,把空间中的物体最终集合到一个一个的单元当中,这样以单元为单位,如果该单元的父节点不可见,则就不需要渲染该单元的物体了,如果可见,递归遍历子节点,进行遮挡查询,如果是叶子节点,直接渲染,对于复杂的场景,减少了遮挡查询的次数。

6.4.2 停滞

它和最基本的遮挡查询都有一个共同的缺点,就是停滞,在返回一个几何实例的遮挡结果时,CPU停滞,等待GPU查询,当返回结果时,GPU停滞。

6.4.3 查询额外的开销

我们查询的是先查询父节点,再查询子节点,对于场景中大多数不可见的物体,减少了查询的次数,但是如果都是可见的物体,则增加了查询次数,因为要遍历整个单元内的所有物体。

6.5 针对上面两个问题的优化

6.5.1 猜测

我们根据上一帧的结果,猜测下一帧的结果,但是这有可能猜错,所以需要后面的校正,这就消除了停滞的问题,也就是每时每刻都让CPU和GPU有事干,当查询结果和当前结果不对时,校正当前的结果。

结果无非就两种情况,实际可见,预测不可见,这需要在遮挡查询的时候校正;实际不可见,预测可见,这种情况渲染的结果是正确的,只不过要多花费点时间。

6.5.2 提升

为了解决查询数目的问题,就是要减少查询的节点数量

我们只查询上一帧看的见得的叶子结点,以及层中有可能看得见的节点,如果一个节点的父节点被遮挡了,就不用查询该节点了。

6.5.3 算法

我们用一个DP表来记录已经查询过得节点的遮挡结果,当我们遮挡查询时,先看表里是否已有记录,同时如果遮挡查询的结果和表里的不一致,要马上更新DP表

 我们通过查询的节点能够倒推出父节点的遮挡结果。减少了查询的次数