基础知识:
最近抽空研究了下半透明物件渲染相关问题,通常我们都知道半透明渲染需要在不透明物件渲染后从远到近渲染半透物件,关闭深度写入。
在不透明物件之后渲染原因:假设不控制渲染顺序,两个物件A为不透,B为半透,先A再B,管线可以再A渲染完更新颜色缓冲,从而正常混合,而先B再A由于颜色缓冲并未更新成A的颜色就会出现错误的最终颜色。
关闭深度写入的原因:不透明的物件间存在穿插的部分,由于无法逐像素控制渲染顺序,减少完全不渲染因深度写入造成的深度测试失败的部分。
虽然关闭深度写入可以解决深度测试失败造成的问题,但并未解决混合的次序问题,造成穿插时候颜色混合的最终解决结果异常,而目前流行的像OIT,ODT就是为了解决穿插时的混合异常问题提出的方案。
正篇:
为了想办法处理半透混合异常问题,先后研究实现过方案1(WeightBlend),方案二(depthPeeling),方案三(perpixel linklist)这几种做法原理网上的介绍挺多,这里记录下原理以及自己使用URP实现的方式:
一、方案1(WeightBlend):
1、介绍:是顺序无关的方案,目前比较流行的就是论文上实现的一套:不透物件正常渲染,针对每个半透物件根据透明度和片元离相机距离作为因子做混合得到最终颜色,这里还是贴下网上流传的公式:
2、实现:
a、为了可以指定部分物件使用OIT,添加一layermask:OIT_Transparent,并再urp的setting中Transparent选项里去除该layermask。
b、物件shader中添加lightMode为OIT-C的pass,该pass混合模式指定为one one,frag中输出Color*W,目的为了算出
c、物件shader中添加LightMode为OIT-A的pass,该pass混合模式指定为one one,frag中输出A*W,目的是为了算出
d、物件shader中添加LightMode为OIT-OA的pass,该pass,mask RGB,混合模式alpha混合指定为zero, one - srcAlpha,frag中输出A*W,目的是为了算出
e、使用一shader合并 b-d三项得到最终半透颜色再与不透物件的颜色做合并
二、方案二(depthPeeling)
1、介绍:该方案是nv最早提出的,原理也相对简单,利用网上的说法就说剥洋葱,一层层的拨开,实际上就是计算每一层的深度,把深度图理解成场景的第一层深度图,那第二层深度就是排除掉第一层深度的那些片元所渲染出的深度,同理第三层到第N层。。 最后根据深度从远到近正常混合。
2、实现
1、总的实现围绕一个循环展开,N层对应N次循环,每个循环里利用上一层深度图,frag中clip掉深度与从上一层深度图采样得出的深度相同的片元。
2、最终将各层color和alpha 正常合并输出。
三、方案三 (perpixel linklist)
1、介绍:看名字应该能猜到其实就是屏幕每个像素都对应有一个链表用来记录该像素所有半透颜色和alpha,接着最后再做混合。
2、实现
暂未实现。
四、总结
几个处理半透渲染顺序的问题相当于被我用来作为上手URP管线修改的工具了,期间也遇到了不少管线使用上的坑,unity有关cammandbuffer的使用API蛮多,容易用错,自己实现过一遍后发现一些不懂的或者要注意的,在这里总结几点经验及教训:
1、Blit的时候有cmd.blit 还有RenderUtil.Blit,应该使用的是后者
2、DrawRender前设置渲染目标的时候cmd.SetTarget.设置完要先cmd.ExecuteCammand,接着cmd.Clear,才能正常将RT切到SetTarget所指定的。
3、内置的colorbuffer和depthbuffer,可以再setup customPass的时候从ScriptRenderer取出传入
4、R16,R32格式作为framebuffer再手机上可能存在兼容性问题,renderdoc profiler的时候会提示InvalidFrameBuffer。
5、遇到深度相关问题也倒腾了不少时间,如
a、Unity内置了ComputeLearDepth和Compute01Depth为了从深度图中算出视空间下的深度是已经考虑了平台差异
b、frag中的input.pos.z只是clip空间下的z,要自行除w,而且得到的是近处为1,远处为0.
c、Unity中vertex中view空间下的深度是负数,取得后要做abs
d、cmd.configTarget 可以指定输出的depth,但是要注意MSAA的影响
e、深度再不同平台精度会有差异,指定同一个格式Editor下用renderdoc调试会发现dx11为D32S8和openGL为D24S8.
f、遇到深度时,考虑放大深度值,乘上一大数,可以利用MRT来做,以此来避免自行处理深度测试和写入,考虑手机平台贴图格式的限制,可以将深度压缩成多个通道存储,使用时自行解压。