【原】实时渲染中经常使用的几种Rendering Path

【原】实时渲染中经常使用的几种Rendering Path

本文转载请注明出处 —— polobymulberry-博客园html

本文为个人图形学大做业的论文部分,介绍了一些Rendering Path,比较简单,若有错误,请你们指正。原文pdf:请点击此处下载编程

1. rendering path的技术基础


在介绍各类光照渲染方式以前,首先必须介绍一下现代的图形渲染管线。这是下面提到的几种Rendering Path的技术基础。缓存

image

目前主流的游戏和图形渲染引擎,包括底层的API(如DirectX和OpenGL)都开始支持现代的图形渲染管线。现代的渲染管线也称为可编程管线(Programmable Pipeline),简单点说就是将之前固定管线写死的部分(好比顶点的处理,像素颜色的处理等等)变成在GPU上能够进行用户自定义编程的部分,好处就是用户能够自由发挥的空间增大,缺点就是必须用户本身实现不少功能。数据结构

下面简单介绍下可编程管线的流程。以OpenGL绘制一个三角形举例。首先用户指定三个顶点传给Vertex Shader。而后用户能够选择是否进行Tessellation Shader(曲面细分可能会用到)和Geometry Shader(能够在GPU上增删几何信息)。紧接着进行光栅化,再将光栅化后的结果传给Fragment Shader进行pixel级别的处理。最后将处理的像素传给FrameBuffer并显示到屏幕上。框架

2. 几种经常使用的Rendering Path


Rendering Path其实指的就是渲染场景中光照的方式。因为场景中的光源可能不少,甚至是动态的光源。因此怎么在速度和效果上达到一个最好的结果确实很困难。以当今的显卡发展为契机,人们才衍生出了这么多的Rendering Path来处理各类光照。函数

2.1 Forward Rendering

image

Forward Rendering是绝大数引擎都含有的一种渲染方式。要使用Forward Rendering,通常在Vertex Shader或Fragment Shader阶段对每一个顶点或每一个像素进行光照计算,而且是对每一个光源进行计算产生最终结果。下面是Forward Rendering的核心伪代码[1]。工具

For each light:
    For each object affected by the light:
        framebuffer += object * light

好比在Unity3D 4.x引擎中,对于下图中的圆圈(表示一个Geometry),进行Forward Rendering处理。post

image

将获得下面的处理结果优化

image

也就是说,对于ABCD四个光源咱们在Fragment Shader中咱们对每一个pixel处理光照,对于DEFG光源咱们在Vertex Shader中对每一个vertex处理光照,而对于GH光源,咱们采用球调和(SH)函数进行处理。spa

Forward Rendering优缺点

很明显,对于Forward Rendering,光源数量对计算复杂度影响巨大,因此比较适合户外这种光源较少的场景(通常只有太阳光)。

可是对于多光源,咱们使用Forward Rendering的效率会极其低下。由于若是在vertex shader中计算光照,其复杂度将是 ,而若是在fragment shader中计算光照,其复杂度为 。可见光源数目和复杂度是成线性增加的。

对此,咱们须要进行必要的优化。好比

  • 1.多在vertex shader中进行光照处理,由于有一个几何体有10000个顶点,那么对于n个光源,至少要在vertex shader中计算10000n次。而对于在fragment shader中进行处理,这种消耗会更多,由于对于一个普通的1024x768屏幕,将近有8百万的像素要处理。因此若是顶点数小于像素个数的话,尽可能在vertex shader中进行光照。
  • 2.若是要在fragment shader中处理光照,咱们大可没必要对每一个光源进行计算时,把全部像素都对该光源进行处理一次。由于每一个光源都有其本身的做用区域。好比点光源的做用区域是一个球体,而平行光的做用区域就是整个空间了。对于不在此光照做用区域的像素就不进行处理。可是这样作的话,CPU端的负担将加剧,由于要计算做用区域。
  • 3.对于某个几何体,光源对其做用的程度是不一样,因此有些做用程度特别小的光源能够不进行考虑。典型的例子就是Unity中只考虑重要程度最大的4个光源。

2.2 Deferred Rendering

 

image

Deferred Rendering(延迟渲染)顾名思义,就是将光照处理这一步骤延迟一段时间再处理。具体作法就是将光照处理这一步放在已经三维物体生成二维图片以后进行处理。也就是说将物空间的光照处理放到了像空间进行处理。要作到这一步,须要一个重要的辅助工具——G-Buffer。G-Buffer主要是用来存储每一个像素对应的Position,Normal,Diffuse Color和其余Material parameters。根据这些信息,咱们就能够在像空间中对每一个像素进行光照处理[3]。下面是Deferred Rendering的核心伪代码。

For each object: 
    Render to multiple targets 
For each light: 
    Apply light as a 2D postprocess

下面简单举个例子[1]。

首先咱们用存储各类信息的纹理图。好比下面这张Depth Buffer,主要是用来肯定该像素距离视点的远近的。

image

图. Depth Buffer

根据反射光的密度/强度分度图来计算反射效果。

image

图.Specular Intensity/Power

下图表示法向数据,这个很关键。进行光照计算最重要的一组数据。

image

图.Normal Buffer

下图使用了Diffuse Color Buffer。

image

图.Diffuse Color Buffer

这是使用Deferred Rendering最终的结果。

image

图.Deferred Lighting Results

Deferred Rendering的最大的优点就是将光源的数目和场景中物体的数目在复杂度层面上彻底分开。也就是说场景中不论是一个三角形仍是一百万个三角形,最后的复杂度不会随光源数目变化而产生巨大变化。从上面的伪代码能够看出deferred rendering的复杂度为 。

可是Deferred Rendering局限性也是显而易见。好比我在G-Buffer存储如下数据

Depth

R32F

Normal + scattering

A2R10G10B10

Diffuse color + emissive

A8R8G8B8

Other material parameters

A8R8G8B8

这样的话,对于一个普通的1024x768的屏幕分辨率。总共得使用1024x768x128bit=20MB,对于目前的动则上GB的显卡内存,可能不算什么。可是使用G-Buffer耗费的显存仍是不少的。一方面,对于低端显卡,这么大的显卡内存确实很耗费资源。另外一方面,若是要渲染更酷的特效,使用的G-Buffer大小将增长,而且其增长的幅度也是很可观的。顺带说一句,存取G-Buffer耗费的带宽也是一个不可忽视的缺陷。

对于Deferred Rendering的优化也是一个颇有挑战的问题。下面简单介绍几种下降Deferred Rendering存取带宽的方式。最简单也是最容易想到的就是将存取的G-Buffer数据结构最小化,这也就衍生出了light pre-pass方法。另外一种方式是将多个光照组成一组,而后一块儿处理,这种方法衍生了Tile-based deferred Rendering。

2.2.1 Light Pre-Pass

Light Pre-Pass最先是由Wolfgang Engel在他的博客[2]中提到的。具体的作法是

  • (1)只在G-Buffer中存储Z值和Normal值。对比Deferred Render,少了Diffuse Color, Specular Color以及对应位置的材质索引值。
  • (2)在FS阶段利用上面的G-Buffer计算出所必须的light properties,好比Normal*LightDir,LightColor,Specular等light properties。将这些计算出的光照进行alpha-blend并存入LightBuffer(就是用来存储light properties的buffer)。
  • (3)最后将结果送到forward rendering渲染方式计算最后的光照效果。

相对于传统的Deferred Render,使用Light Pre-Pass能够对每一个不一样的几何体使用不一样的shader进行渲染,因此每一个物体的material properties将有更多变化。这里咱们能够看出相对于传统的Deferred Render,它的第二步(见伪代码)是遍历每一个光源,这样就增长了光源设置的灵活性,而Light Pre-Pass第三步使用的实际上是forward rendering,因此能够对每一个mesh设置其材质,这二者是相辅相成的,有利有弊。另外一个Light Pre-Pass的优势是在使用MSAA上颇有利。虽然并非100%使用上了MSAA(除非使用DX10/11的特性),可是因为使用了Z值和Normal值,就能够很容易找到边缘,并进行采样。

下面这两张图,左边是使用传统Deferred Render绘制的,右边是使用Light Pre-Pass绘制的。这两张图在效果上不该该有太大区别。

image image

2.2.2 Tile-Based Deferred Rendering

TBDR主要思想就是将屏幕分红一个个小块tile。而后根据这些Depth求得每一个tile的bounding box。对每一个tile的bounding box和light进行求交,这样就获得了对该tile有做用的light的序列。最后根据获得的序列计算所在tile的光照效果。[4][5]

对比Deferred Render,以前是对每一个光源求取其做用区域light volume,而后决定其做用的的pixel,也就是说每一个光源要求取一次。而使用TBDR,只要遍历每一个pixel,让其所属tile与光线求交,来计算做用其上的light,并利用G-Buffer进行Shading。一方面这样作减小了所需考虑的光源个数,另外一方面与传统的Deferred Rendering相比,减小了存取的带宽。

2.3 Forward+

Forward+ == Forward + Light Culling[6]。Forward+很相似Tiled-based Deferred Rendering。其具体作法就是先对输入的场景进行z-prepass,也就是说关闭写入color,只向z-buffer写入z值。注意此步骤是Forward+必须的,而其余渲染方式是可选的。接下来来的步骤和TBDR很相似,都是划分tiles,并计算bounding box。只不过TBDR是在G-Buffer中完成这一步骤的,而Forward+是根据Z-Buffer。最后一步其实使用的是forward方式,即在FS阶段对每一个pixel根据其所在tile的light序列计算光照效果。而TBDR使用的是基于G-Buffer的deferred rendering。

实际上,forward+比deferred运行的更快。咱们能够看出因为Forward+只要写深度缓存就能够,而Deferred Render除了深度缓存,还要写入法向缓存。而在Light Culling步骤,Forward+只须要计算出哪些light对该tile有影响便可。而Deferred Render还在这一部分把光照处理给作了。而这一部分,Forward+是放在Shading阶段作的。因此Shading阶段Forward+耗费更多时间。可是对目前硬件来讲,Shading耗费的时间没有那么多。

image

Forward+的优点还有不少,其实大多就是传统Forward Rendering自己的优点,因此Forward+更像一个集各类Rendering Path优点于一体的Rendering Path。

image

3. 总结


首先咱们列出Rendering Equation,而后对比Forward Rendering,Deferred Rendering和Forward+ Rendering[6]。

3.1 Rendering Equation

其中点 处有一入射光,其光强为 ,入射角度为 。根据函数 和 来计算出射角为 处的出射光强度。最后在辅以出射光的相对于视点可见性 。注意此处的 为场景中总共有 个光源。

image

3.2 Forward Renderng

因为Forward自己对多光源支持力度不高,因此此处对于每一个点 的处理再也不考虑全部的 个光源,仅仅考虑少许的或者说通过挑选的 个光源。能够看出这样的光照效果并不完美。另外,每一个光线的 是计算不了的。

image

3.3 Deferred Rendering

因为Deferred Rendering使用了light culling,因此不用遍历场景中的全部光源,只需遍历通过light culling后的 个光源便可。而且Deferred Rendering将计算BxDF的部分单独分出来了。

image

3.4 Forward+ Rendering

能够看出Forward+和Forward最大区别就是光源的挑选上有了很到改进。

image

参考文献

[1] Shawn Hargreaves. (2004) “Deferred Shading”. [Online] Available:

http://hall.org.ua/halls/wizzard/books/articles-cg/DeferredShading.pdf (April 15,2015)

[2] Wolfgang Engel. (March 16, 2008) “Light Pre-Pass Renderer”. [Online] Available:

http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html(April 14,2015)

[3] Klint J. Deferred Rendering in Leadwerks Engine[J]. Copyright Leadwerks Corporation, 2008.

[4] 龚敏敏.(April 22, 2012) “Forward框架的逆袭:解析Forward+渲染”. [Online] Available:

http://www.cnblogs.com/gongminmin/archive/2012/04/22/2464982.html(April 13,2015)

[5] Lauritzen A. Deferred rendering for current and future rendering pipelines[J]. SIGGRAPH Course: Beyond Programmable Shading, 2010: 1-34.

[6] Harada T, McKee J, Yang J C. Forward+: Bringing deferred lighting to the next level[J]. 2012.

相关文章
相关标签/搜索