方向光源是最不消耗GPU资源的。点光源的阴影较消耗GPU资源。聚光灯是较消耗GPU资源的。
Area light区域光源无法用于实时光照,只适用于光照贴图烘培。
Cookie如果是平行光或聚光灯 那么可以是2D的纹理遮罩贴图,如果是点光源那么cookie要是cubemap texture立方体纹理。
unity3d渲染路径:
只有平行光在forward渲染模式下支持阴影,其它光源类型在player setting中设置Rendering path 为deferred模式下(4.x版本非移动平台才支持)才能支持阴影。
1.vertex list rendered path:
硬件要求:Opengl es1.1以上
就顶点光照颜色计算,颜色可进行插值,不支持像素运算效果,如法线贴图,阴影,Light cookies等。
2.forward rendered path:
硬件要求:Opengl es2.0 shader modle2.0(DX9)以上
核心处理伪代码:
For each light:
For each object affected
by the light:
framebuffer += object * light
算法复杂度:O(n^2)。
只有一个渲染目标(后台颜色缓存),支持有限的像素级别光照(通常最亮的为important, 其它设置为not important),最多4个顶点光照,其它是SH近似光照计算。
假设有ABCD EFGH 由近到远围绕着物体。
也就是说,对于ABCD四个光源我们在Fragment Shader中我们对每个pixel处理光照,对于DEFG光源我们在Vertex Shader中对每个vertex处理光照,而对于GH光源,我们采用球调和(SH)函数进行处理。
渲染过程每个pass都是一个完整渲染drawcall:
Bass Pass:
渲染一个像素计算的方向光。以及所有顶点计算和SH方向光。之后每增加一个逐像素计算的光都需要增加一个对应的pass.LightMap在此应用,在此步的方向光可以有阴影。
Additional Pass:
额外的像素计算光,无阴影。也就是说Forward Lighting只支持一个有阴影的方向光。
SH:
Spherical Harmonics,效率极高,不支持light cookies和法线贴图,更新频率慢,不支持镜面反射。
缺点是:每增加一个灯光,就要增加相应的draw call渲染物体,当灯光数量很多场景很复杂时候CPUGPU计算压力很大。
优点是:每个灯光渲染一次物体,物体每次通过多重采样的光栅化,支持抗锯齿;物体每次提交到后台缓存都是独立的,可以进行blend 支持半透明物体的渲染,有限的灯光进行像素级别光照支持法线贴图,平行光阴影,light cookies等。
3.deferred rendered path:
硬件要求:Opengl es3.0 shader modle3.0(DX9c)以上
核心处理伪代码:
For each object:
Render to multiple targets
For each light:
Apply light as a 2D postprocess
算法复杂度:O(n)
Deferred Rendering(延迟渲染)顾名思义,就是将光照处理这一步骤延迟一段时间再处理。具体做法就是将光照处理这一步放在已经三维物体生成二维图片之后进行处理。也就是说将物空间的光照处理放到了像空间进行处理。要做到这一步,需要一个重要的辅助工具——G-Buffer。G-Buffer主要是用来存储每个像素对应的Position,Normal,Diffuse Color和其他Material parameters。根据这些信息,我们就可以在像空间中对每个像素进行光照处理。
Depth
R32F
Normal + scattering
A2R10G10B10
Diffuse color + emissive
A8R8G8B8
Other material parameters
A8R8G8B8
对于一个普通的1024x768的屏幕分辨率,一个G-Buffer共得使用1024x768x128bit=20MB。如果要渲染更酷的特效,使用的G-Buffer大小将增加。存取G-Buffer耗费的带宽也是一个不可忽视的缺陷。
存储的G-buffers中有
所有物体汇总的Z值(一个float 32位),
材质Diffuse Color(可直接来自纹理)
,法线贴图RGB和镜面强度A(色泽),
材质Specular Color(可直接来自纹理),
纹理材质索引值(可以在shader中指定获取)都是是唯一的缓存对象;对每个灯光从光照入射角lightDir,视角方向viewDirection从shader中获取,从不同的缓存G-Buffer中获取对应的纹理漫反射值,法线值,高光颜色值,进行光照的计算合成:
如漫反射颜色:
float3 lightDir = normalize(input.lightDir);
float3 diffuseReflection = attenuation * _LightColor0.rgb * resColor.rgb * max(0.0, dot(normalDir, lightDir));
高光颜色:
计算镜面高光需要viewDir也要转换的切线空间中进行计算(deferred应该是在屏幕空间坐标系中进行运算)。
specularReflection = attenuation * _LightColor0.rgb * _SpecColor.rgb *
pow( max(0.0, dot( reflect(-lightDirection, normalDirection), viewDirection)), _Shininess);
最终光照颜色:
return float4(input.vertexLighting + ambientLighting + diffuseReflection + specularReflection, 1.0);
unity3d perferred渲染过程(其实是类似Light Pre-Pass形式)分为三个处理,组成完整的渲染drawcall:
Base Pass:
绘制深度缓冲等裁剪空间的缓冲信息。储存在一张ARGB32 Render Texture中,RGB存该点法线,A存镜面反射强度。如果深度能读取为Texture的话,深度不会被显式渲染。不能读取的话,则使用着色器替换ShaderReplacement,即Camera.RenderWithShader。Base Pass的结果是,场景的物体附带了
深度缓冲,以及一张储存法线和镜面强度
的Texture。
Lighting Pass:
只支持Blinn-Phong光照模型,阴影也是在这一步计算。之前产生的缓冲在这一步用于计算光照。产生的光照缓冲同样是一张ARGB32 Render Texture,RGB表漫反射光的颜色,A表单色镜面光 。应该对光照信息,可以使用blend混合。
Final Pass:
将纹理颜色(来自外部文件的纹理贴图组合的大图)与储存的光照结果组合。LightMap在这一步被应用 注意:在Deferred Lighting的这一步完成后,才会到Forward Lighting。应该存在每个物体的z值和normal值可以很简单的找到边缘进行MSAA抗锯齿操作。
缺点:
纹理合并为大图,不真正支持反锯齿(大图边界不清楚),不支持半透明(大图在光照颜色前面合成,光照的alpha不能再参与混合)。如果是类似Light Pre-Pass形式应该可以支持部分抗锯齿和半透明处理的。硬件至少是Shader Model 3.0 。延迟渲染在正交摄像机模式下也是不支持的,如果摄像机切换到了正交模式之后就会默认使用正向渲染。
优点:对复杂场景,很多光源情况下,有很好的渲染性能。
其它的deferred lighting渲染优化方式:
1).Light Pre-Pass
具体的做法是:
(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绘制的。这两张图在效果上不应该有太大区别。
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相比,减少了存取的带宽。
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耗费的时间没有那么多。Forward+的优势还有很多,其实大多就是传统Forward Rendering本身的优势,所以Forward+更像一个集各种Rendering Path优势于一体的Rendering Path。