Deferred Shading
本篇博客说一下学习延迟渲染中遇到的问题。
学习延迟渲染的过程中主要给了我疑惑的是深度测试的问题,在Learn OpenGL的相关代码中,我并没有看到G-buffer有根据片段深度对片段进行消隐或者显示。这是我一直疑惑的地方。如果在fragment shader中不经过深度测试就将片段输出至G-buffer相对应的纹理附件上的话,我认为会出现一些问题(因为纹理附件是2D的,在纹理附件上的一点不可能产生2个RGB值,这就说明我们需要在fragment shader中进行深度测试)。
言归正传,先来说说延迟渲染的概念。
在讲解延迟渲染之前,我们首先要知道到目前为止用的渲染都是正向渲染(forward rendering或者forward shading),即对于一个有多个物体以及一个光源的场景,我们会一个物体一个物体(无序)地去渲染(计算光照)。对于一个光源来说这可能没什么问题(事实上之前的学习中用到的光源不超过10个),然而如果是多个光源的话,问题就大了:由于多个光源下要进行的计算量很大(要逐个片段计算光照,然后还得将各个光源的光照效果结合起来),如果我们还是一个物体一个物体地去渲染,那么就有可能因为片段遮挡(片段深度测试)的问题导致一些计算白白浪费掉;并且因为计算量大,帧率也会下降。
于是乎就出现了延迟渲染(deferred shading),延迟渲染改掉了“先光照计算,再深度测试”的顺序,变成了“先深度测试,再进行光照计算”。伪码如下:
Pass 1 { //第一个Pass不进行光照计算,仅存储光照计算所需信息到G缓冲中 for (each primitive in this model) { for (each fragment covered by this primitive) { if (failed in depth test) { discard;//若没有通过深度测试,则该片元不可见 } else { //若该片元可见,则进行光照计算,则将相关信息存储到G缓冲中 writeBuffer(materialInfo, pos, normal, lightDir, viewDir); } } } }
Pass 2 {
//利用G缓冲中的信息进行真正的光照计算
for (each pixel in the screen) {
if (the pixel is valid) {
//若该像素有效,则读取其G缓冲中的信息
readBuffer(pixel, materialInfo, pos, normal, lightDir, viewDir);
//根据读取到的信息进行光照计算
float4 color = Shading(materialInfo, pos, normal, lightDir, viewDir);
//更新帧缓冲
writeFrameBuffer(pixel, color);
}
}
}
这样子就能避免不必要的光照计算了。值得一提的是,当光源数目不多的时候,有可能正向渲染的开销要小于延迟渲染,并且延迟渲染不能使用MSAA和Blending
延迟渲染的优点是可以在大量渲染光照的时候不消耗大量的内存,然而并不能认为这它可以无限制地渲染光照,更多情况下,(由于光线衰弱等原因)我们会计算光照的“作用半径”,也就是光能到达的范围,称之为光体积(light volumes),来从根本上节省光照计算的开销。
具体如何操作这里就不写了,详情参照Learn OpenGL的相关章节即可。
参考资料:
[1]【《Real-Time Rendering 3rd》 提炼总结】(七) 第七章续 · 延迟渲染(Deferred Rendering)的前生今世
以上