前向渲染&延迟渲染
目录
读书是在别人思想的帮助下,建立起自己的思想。 ——鲁巴金
在unity中,渲染路径决定了光照是如何应用到Unity Shader中的。我们只有为Shader正确的选择和设置需要的渲染路径,该Shader的光照计算才能被正确的执行。
LightMode标签设置
Unity Shader 中, Pass 的LightMode标签支持的渲染路径设置选项:
Always : 不管使用哪种渲染路径, 该 Pass 总是会被渲染, 但不会计算任何光照。
ForwardBase:用于前向渲染。该Pass会计算环境光、最重要的平行光、逐顶点/SH光源和Lightmaps。
ForwardAdd: 用于前向渲染。该Pass会计算额外的逐像素光源。每个 Pass 对应一个光源。
Deferred:用于延迟渲染。该Pass会渲染G缓冲(G-buffer)。
ShadowCaster: 把物体的深度信息渲染到阴影映射纹理(shadowmap)或一张深度纹理中。
PrepassBase: 用于遗留的延迟渲染。该 Pass 会渲染发现和高光反射的指数部分。
PrepassFinal: 用于遗留的延迟渲染。该Pass通过合并纹理、光照和自发光来渲染得到最后的颜色。
Vertex、VertexLMRGBM 和 VertexLM : 用于遗留的顶点照明渲染。
前向渲染(Forward Rendering)。
核心思想:每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个是颜色缓冲区,一个是深度缓冲区。我们通过深度缓冲区判断一个片元是否是可见的,如果可见的话就更新颜色缓冲区中的颜色值。
- 无效计算— 前向渲染对一个几何体进行一系列的计算,对于多个物体,一个几何体处理完之后才会进行下一个几何体的处理。所以我们就可以发现问题:有太多的无效渲染。
- 计算量— 同时,如果光照特别多,每个几何体都要对这些光照计算一遍的话计算量非常大。针对这些缺点,后来也有一系列的解决方式,如Early-z 、Z-Prepass 、 Hi-Z 等剔除方法。
- 透明处理— 可以处理处理半透明物体。
前向渲染路径中有三种处理关照的方式:
逐顶点处理、逐像素处理、球谐函数(Spherical Harmonics, SH)处理。而决定一个灯光是哪种处理模式取决于它的类型和模式:
- 场景中最亮的平行光总是按照逐像素处理的。
- 渲染模式被设置成Not Important的光源,会按逐顶点或者SH处理。
- 渲染模式被设置成Important的光源,会按逐像素处理。
- 如果逐像素光源数量小于Quality Setting中的逐像素光照数量,会有更多的光源按照逐像素的方式渲染。
内置的光照变量和函数:
在UnityShaderVariables.cginc文件中,我们可以找到Unity提供的和处理光照有关的变量:
CBUFFER_START(UnityLighting)
#ifdef USING_DIRECTIONAL_LIGHT
uniform fixed4 _WorldSpaceLightPos0;
#else
uniform float4 _WorldSpaceLightPos0;
#endif
uniform float4 _LightPositionRange; // xyz = pos, w = 1/range
// Built-in uniforms for "vertex lights"
float4 unity_4LightPosX0; // x coordinates of the 4 light sources in world space
float4 unity_4LightPosY0; // y coordinates of the 4 light sources in world space
float4 unity_4LightPosZ0; // z coordinates of the 4 light sources in world space
float4 unity_4LightAtten0; // scale factors for attenuation with squared distance
float4 unity_LightColor[8]; // array of the colors of the 4 light sources
float4 unity_LightPosition[8]; // apparently is not always correctly set
// x = -1
// y = 1
// z = quadratic attenuation
// w = range^2
float4 unity_LightAtten[8]; // apparently is not always correctly set
float4 unity_SpotDirection[8];
// SH lighting environment
float4 unity_SHAr;
float4 unity_SHAg;
float4 unity_SHAb;
float4 unity_SHBr;
float4 unity_SHBg;
float4 unity_SHBb;
float4 unity_SHC;
CBUFFER_END
在UnityCG.cginc可以找到光照处理辅助函数:
// Computes world space light direction
inline float3 WorldSpaceLightDir( in float4 v );
// Computes object space light direction
inline float3 ObjSpaceLightDir( in float4 v );
// Computes world space view direction
inline float3 WorldSpaceViewDir( in float4 v );
// Computes object space view direction
inline float3 ObjSpaceViewDir( in float4 v );
float3 Shade4PointLights (
float4 lightPosX, float4 lightPosY, float4 lightPosZ,
float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3,
float4 lightAttenSq,
float3 pos, float3 normal);
float3 ShadeVertexLights (float4 vertex, float3 normal);
// normal should be normalized, w=1.0
half3 ShadeSH9 (half4 normal);
延迟渲染(Deferred Rendering)
较于前向渲染,延迟渲染不同的是先进行可见性判断,接着进行光照计算。延迟渲染除了颜色缓冲区、深度缓冲区,还使用了G缓冲区(G-buffer),在这里面存储着我们需要的信息,并会根据这里面的信息进行光照计算。也就是说,先确定渲染到屏幕上的像素,再用像素去做光照。
- 有效计算 — 当场景中所有几何体都写入G-Buffer之后,==不管场景中有多少物体、多少三角形,光照计算都已经和它们没有关系了!!!==所以,只渲染可见的像素,不会有无效的计算。
- 显卡要求 — 所以,延迟渲染很适合场景中光源数目比较多的情况,而且每个光源都可以按逐像素的方式进行处理。但是,这也就说明了延迟渲染对显卡的要求比较高。
- ==抗锯齿 ==— 因为延迟渲染相当于是render to texture,如果使用MSAA的话,会消耗大量的带宽。所以延迟渲染无法使用多重采样抗锯齿(MSAA)。
- 透明处理 —不能处理半透明物体。多个半透明对象可以覆盖同一个屏幕像素,而G-Buffer中每个像素只可以存储一个值。在光照Pass中,深度值,表面法线,diffuse颜色和高光色被当前的屏幕像素所采样来进行光照,因为每个G-Buffer只能采样一个值,透明物体不能光照pass中被支持。延迟渲染的时候,一般都是先渲染非透明的Mesh,后续再单独渲染透明Mesh。
- 模型使用同一个光照Pass — 当mesh渲染到Gbuffer之后,我们已经不知道哪个像素点属于哪个mesh了,只能使用同一套光照计算。
因为文章开头的那句话,在阅读时更加注重自己的思考,文中有错误之处,烦请大家批评指正!不胜感激!!