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)的前生今世

[2] Deferred Rendering基础篇(一)

以上

posted @ 2019-12-03 12:57  jckcoenf  阅读(266)  评论(0编辑  收藏  举报