转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2233
http://dogasshole.iteye.com/blog/1429665
http://www.gdcvault.com/
2009年AMD在发布HD 5800的时候也发布了一个Order Independent Transparency(OIT)的demo,但只有介绍,没有多少可以参考的东西。GDC 2010上的OIT and GI using DX11 linked lists才给出了比较完整的算法细节。虽说这几年也有不少新的OIT算法出现,但作为具有标杆意义的OIT算法,Per-Pixel Linked Lists还是值得实现到KlayGE的开发版本中,以做对比。
算法
顾名思义,Per-Pixel Linked Lists的意思就是每个pixel上一个链表,存放属于该pixel的所有fragment。这种不均匀的数据结构对GPU来说是很要命的。
在Per-Pixel Linked Lists中,链表需要两个额外的buffer,一个称为fragments buffer,需要是屏幕尺寸的N倍,负责存放所有的fragment;另一个是start offset buffer,和屏幕尺寸相同,存放每个pixel的链表队头。构造出存储的数据结构后,算法本身就变得很简单了,只有两步:
- PS计算出shading后的颜色,让fragments buffer自带的计数器加一,得到一个空间后把颜色和深度存进去,同时更新该像素位置对应的start offset buffer。
- 在post process里,PS从start offset buffer读到队头,由此索引这个pixel的整个链表,根据深度进行排序,然后按顺序做alpha blending。
由此可见,该算法只需要在原有流水线PS里加上几行,同时多一个全屏post process即可完成。所有的fragment只需要经过PS一次,绝无浪费。相对于以前流行的OIT方法Depth Peeling来说,在相同层数的情况下,Per-Pixel Linked Lists的结果与其完全相同,并没有近似计算,但理论性能要高得多。因为Depth Peeling如果要peeling N层,所有的fragment就要生成N次,并丢弃大部分fragment,就剩下需要剥离的那层fragment。
实际测试的结果也证实了之前的分析,同样的结果,在NVS 4200M上,Per-Pixel Linked Lists可以跑到62.47FPS,而Depth Peeling只能46.05FPS。
限制
当然,Per-Pixel Linked Lists至少要在D3D11的硬件上才能实现。之前的硬件不支持PS写入UAV,也没有附在buffer上的原子计数器。所以除非用GPGPU的方法实现一个软件光栅化,否则没法绕开这些限制。
另一个明显的限制来自于空间占用。因为无法事先知道链表会有多长,fragments buffer只能申请一个比较大的空间,可能会浪费不少,也可能会溢出。而且因为fragment添加的顺序是乱的,没法像Depth Peeling那样只要前几层。所以,这个方法的空间消耗是不可控的。
除了OIT还能做什么
理论上,所有非近似的OIT方法,都能用来做voxelization。在去年的一篇blog未来属于SVO?中就提到了如何用从conservative rasterize配合Per-Pixel Linked Lists,在一个pass内直接把mesh转成voxel表达。
由于存储了场景的所有fragment,甚至可以直接在里面做光线跟踪。不过显然这么做不如就用SVO那套框架有效率了。
http://dogasshole.iteye.com/blog/1429665
http://www.gdcvault.com/这里可以下到。
per pixel link list可以做到order independent translucency rendering。
之前一直恶心大家的这个东西终于可以在dx11干掉了,screenshot:
纠缠api没什么意思,比较有意义的是可以做到可以给每个pixel建立一个linklist这件事情:
这个太nice了,除了order independent translucency,很多酷的算法都可以做到了像:translucency deferred lighting等。
这个性能消耗估计也会“物有所值”了。