基于directX11的OIT实现浅谈
dx游戏编程吧
全部回复
仅看楼主
level 5
匿迹丶忆 楼主
放假后一直想写这篇文章,刚放假玩了几天,好吧,把放假前积攒的知识印象都浅化了。前几天又重拾directX11,现在终于能开始写了,虽然没人看吧......
2013年07月19日 16点07分 1
level 5
匿迹丶忆 楼主

由于百度写代码实在是让人头疼,所以我尽量不用代码。
OIT全称Order Independent Transparency,在进行物体透明度混合的时候我们时常要关注透明部分的深度值,因为虽说是3D,其实画图还是画在2DTexture上的,所以透明部分要按照深度值排序渲染。
3D行业发展到现在OIT的实现方法肯定很多,我这里使用的是参考别人的和SDK里面的OIT Sample自己改进的一个方法(我会在最后给出参考资料的链接),由两个PASS完成,你要是跟我说有一个PASS就能完成的OIT那是不可能的,单纯的关闭深度测试来实现称不上是OIT。
下面介绍的方法可以实现多种输入流格式的物体色彩半透明混合,而且考虑了背景色,至于深度值重写下面我再详细介绍(虽然能够实现,但是我有更好的方法,只是还没写而已= =)
先创建两张缓存
一张FLBuffer是用来存放(深度,颜色(包含透明度)和下一个结点的地址)的结构缓存,没错,我将要使用链表,我们将为窗口中每个点创建一条链表,但仅靠它是不能完成链表的功能的,所以有第二张缓存startOffserBuffer,这是一张和窗口等大的缓存,它对应窗口上的每一个像素点,存放的是这个像素点位置上链表的起始位置,以数组的下标代替地址。
然后用这2张缓存创建4张视图,分别为
FLBufferSRV,startOffsetBufferSRV
FLBufferUAV,startOffsetBufferUAV
SRV是支持读的视图
UAV是支持读和写的视图
它们的数据是和创建它所用的Buffer相关联的,因此我们可以在FLBuffer需要写的时候时候它的UAV,需要读的时候使用SRV,这样能加快CPU读取速度。
另外链表的结构如下
在Pass1里,我们正常写入顶点数据,但在经过PS的时候,把深度测试设置成测试但不写入,我们把数据储存到FLBuffer里面,然后使用clip(-1),因此不必解绑RenderTargetView也没关系,clip(-1)会丢弃这个像素点,不过我们已经事先把像素点存到了缓存里面,位置为从FLBuffer[0]开始存到渲染结束.....突然发现这里要讲好多东西= =蛋疼
2013年07月19日 16点07分 2
level 5
匿迹丶忆 楼主
会看这篇文章的想必也不是白痴,我直接说原理得了。。
2013年07月19日 16点07分 3
level 5
匿迹丶忆 楼主

将所有输入的像素点的信息都存到FLBuffer里面,并且startOffserBuffer存放了所以链表的表头位置,As an example ,在屏幕的X,Y坐标上startOffserBuffer的值为50
也就是链表的表头为FLBuffer[50],这就获得了这个位置的第一个像素点,把他存到一个临时的数组里面方便后面排序,再由FLBuffer[50]的uNext获得链表下一个位置的像素点21,
FLBuffer[21]就是第二的像素点信息,存入数组,遍历直到遇到uNext=0xffffffff为止(事先设置的初始值,我不说大家也知道要怎么做)
然后把这些像素点进行深度排序,这里可以使用computeShader进行双调排序或者其他并行排序算法,也可以直接在PixelShader上进行普通排序,我参考的资料直接在PixelShader上进行单线程的双调排序......想必是直接复制了SDK里面的Sample,效率反而低了。
排序完成后用PixelShader返回颜色值,进行这一操作的时候可以使用画Quad的方式,这在3D程序里面制作2D画面很经常用到,当然输出也没那么简单,
这里我也对参考的资料做了修改,原本渲染底色是黑色,现在分离透明度计算将涉及到原渲染图层底色的混合,自己做一些数学计算就能知道result.a的值是怎么得到的了,这是普通的混合插值计算,如果想要得到其他效果自己修改一下都可以实现。
2013年07月19日 17点07分 4
level 5
匿迹丶忆 楼主

最后还有深度值,一开始虽然进行了深度检测,但是关闭了深度写入,但这并不是问题,因为我们保存了深度值,之前对数据进行了排序,很容易得到窗口中每点的像素点位置的最低深度值,然后把它写入DepthStencilBuffer中就可以了,我不知道能不能直接写入,但是只要创建深度视图的也创建了一张UAV就可以了,通过UAV写入深度值,于是也不必考虑渲染完半透明物体之后还要渲染其他物体的问题了。而我现在实现的只是通过二次读入顶点来写如深度值,懒了,让我休息几天再优化吧。
2013年07月19日 17点07分 5
level 5
匿迹丶忆 楼主
看物方块内的方块,FPS能达到600+,正常应该能达到800+的才对,刚把OITClass写好还没和原本框架完美结合,代码乱糟糟的,但这方法确实效率很高,而且还能优化下去
2013年07月19日 17点07分 7
话说,你现在用的是甚麽显卡跑这demo?
2013年07月24日 05点07分
level 5
匿迹丶忆 楼主
另外对于没有使用过effect11框架的朋友,为了程序效率,建议不要使用,
如果为了自己写代码效率那无妨,具体的我不想解释了,困了。
2013年07月19日 17点07分 8
level 5
匿迹丶忆 楼主
2013年07月19日 17点07分 9
很好喔~ clip(-1)我就没想到。深度值可不可以直接写入DepthStencilBuffer这事,印象中A卡的58xx似乎不行,有可能是驱动的问题,后面出的卡不知道。通过UAV写入深度值应该各卡通行,没问题。不过,不是会慢上少许吗?
2013年07月24日 05点07分
回复 黑桐 :详情请看下面..
2013年08月06日 14点08分
level 5
匿迹丶忆 楼主
effect11内部做了许多我不想做的事,更重要的是某些部分和PIXWin有冲突。虽然直到几天前effect11还有在更新吧,我承认我用的是SDK里面自带的effect11几年前的版本(其实有些BUG后来有修复),那些BUG我忍了很久了,我不能忍受还有潜在BUG的可能,而且存在无法避免的效率损失。跟着别人屁股走始终不是一件好事,仅此表示本人不再使用effect11。
2013年07月24日 10点07分 10
level 5
匿迹丶忆 楼主
移除了effect framework以后使用PIXWin流程清晰多了,绑定HLSL变量有一些需要注意的地方,对于绑定问题有需要解答的下面回帖吧。
另外不知道是我方法的问题还是DX本身多重采样算法的问题,一旦我开启4倍的多重采样在三角元的边缘会有白线。关闭了就正常,我记得抗锯齿应该是在输出阶段进行的,@#……#算了,想不通,在没找不问题之前OIT部分就不使用多重采样了。
还有我忘记了深度和模版缓存是共用一个缓存区的,因此绑定不了UAV,但是既然不能用UAV修改那肯定有别的方法。方法如下:
在PS函数参数上添加out float depth :SV_DEPTH直接给depth赋值就可以了,我一开始查PIXWin里面的汇编,找到odepth,但结果绕了一大圈才找到:SV_DEPTH这个语义,郁闷
2013年08月04日 15点08分 11
补充一下使用 out float depth :SV_DEPTH修改深度的时候深度检测必须是开启的
2013年08月04日 15点08分
level 5
匿迹丶忆 楼主
下一篇文章如果不出差错的话我估计会讲粒子系统
2013年08月04日 15点08分 12
level 5
匿迹丶忆 楼主
多体问题的sample是个好例子,有些想法可以借鉴
那个多体算法我考虑看要不要山寨= =
2013年08月06日 14点08分 14
level 5
匿迹丶忆 楼主
2013年08月07日 17点08分 15
level 5
匿迹丶忆 楼主
2013年08月07日 17点08分 16
level 5
匿迹丶忆 楼主
还是黑暗中的环境比较好看
另外,我发现OIT排序有个地方弄错了- -
2013年08月07日 17点08分 17
手贱直接复制了那段排序,忘记它没把奇数情况考虑进去,我先把传上去的删了
2013年08月07日 18点08分
level 5
匿迹丶忆 楼主
以上粒子活动计算过程是在CS中进行的,效率快得没话说
2013年08月07日 17点08分 18
level 2
楼主,请问下在进行一次渲染的时候,在投影空间里物体的深度排序是directx自己实现的吗
2015年10月28日 02点10分 20
并不是呀,directX只会根据你传入的顶点顺序进行渲染,渲染的时候对比深度缓存进行消隐(如果有开消隐的话),所以这种情况下一个半透明的物体一次性渲染完,从不同的角度看其实会有不同的效果,可能有的角度先渲染的后面,有的角度先渲染的前面,先渲染前面后面的部分就被消隐了
2015年10月31日 06点10分
回复 匿迹丶忆 :嗯嗯,理解了
2015年10月31日 06点10分
1