3D游戏引擎系列(十):延迟渲染

发表于2017-04-04
评论0 1.2k浏览

        延迟渲染在PC端的游戏开发中使用的非常多,由于其效果非常好,也被很多开发者所喜欢,现在这些技术已经都放开了,作为开发者至少我们要知道其实现原理,代码网上也有很多,读者可在百度中搜索。在这里主要是给读者普及一下关于延迟渲染的使用,我在自己研发的引擎中也是使用了延迟渲染技术。Unity3D引擎也使用了延迟渲染技术,但是由于其在移动端效率问题,一般不会用在项目开发中。

      延迟渲染也是图形学算法中的一种,它是针对场景渲染的,也称为后处理渲染,就是在原有场景渲染基础上再使用延迟算法对其再进行渲染处理,最后显示到屏幕上。下面我们就直奔主题了:

Unity3D中,延迟渲染管线为分两个阶段进行,G-Buffer阶段和光照计算(Lighting)阶段。下面我们使用传统的方法,网上很多说的是四个阶段,我们

的Deferred Lighting使用三个阶段,伪代码如下所示:

[cpp] view plain copy
 
  1. 第一步、 for each object  
  2.    {  
  3.       填充G-Buffer  
  4.    }  
  5. 第二步、 for each light  
  6.    {  
  7.       Lighting pass  
  8.    }  
  9. 第三步、 for each object  
  10.    {  
  11.      执行shading  
  12.    }  

其中,第二步中Lighting pass非常重要,一遍一遍的敲公式非常麻烦,在这里用图片给读者展示一下Lighting Pass的计算公式,数学在游戏开发中占有重要

占有重要地位,对于算法的实现,不是一朝一夕的事情,但是一定要坚持去下面就把Lighting Pass的计算公式给读者展示一下:

Lighting pass在Deferred Lighting框架处于核心地位,在这里我打算先把lighting pass解析清楚。一旦lighting pass表达好了,G-Buffer所需要保存的信息,以及shading pass能得到的信息也都清楚了。

        基于物理的BRDF推出了渲染模型总公式:

再有N个光源的情况下,每个像素的光照响应就是:


对于Deferred shading来说,每一个shading pass就是执行一个


而对于Deferred lighting来说,公式需要重新整理一下:


由于cdiff是到最后的shading pass才计算,所以在每一个light pass里面,diffuse和specular必须分开才能保证结果正确:


为了把diffuse和specular放入4个通道的buffer中,就只能牺牲specular的颜色,只剩下亮度,同时cspec也简化成一个标量。所以,lighting pass的计算成了:


在上述公式中实现了Diffuse,Specular,lightning pass,最后就是对G-Buffer的处理,下面把处理Deferred延迟渲染的完整shader代码给读者展示一下:


[cpp] view plain copy
 
  1. // Materices  
  2. float4x4 g_matWorld         : World;  
  3. float4x4 g_matWorldView     : WorldView;  
  4. float4x4 g_matWorldViewProj : WorldViewProjection;  
  5. float4x4 g_matProjInvert    : ViewProjectionInverse;  
  6. float4x4 g_matTextureProj   : TextureProjection;  
  7.   
  8. // Light  
  9. float4 g_LightAmbient;  
  10. float4 g_LightColor;  
  11. float4 g_LightPosition;  
  12. float4 g_LightPositionViewSpace;  
  13. float  g_LightRadius;  
  14. float  g_OneOverSqrLightRadius;  
  15.   
  16. // Material  
  17. float g_MaterialAmbient;  
  18. float g_MaterialEmissive;  
  19. float g_MaterialDiffuse;  
  20. float g_MaterialSpecular;  
  21. float g_MaterialShininess;  
  22.   
  23. // Textures  
  24. texture g_TextureDiffuse;  
  25. texture g_TextureCubeNormalization;  
  26.   
  27. // Render targets  
  28. texture g_TextureSrcColor;  
  29. texture g_TextureSrcPosition;  
  30. texture g_TextureSrcNormal;  
  31. texture g_TextureSrcMaterial;  
  32.   
  33. // ---------------------------------------------------------------  
  34. // Samplers  
  35. // ---------------------------------------------------------------  
  36.   
  37. sampler g_SamplerDiffuse = sampler_state  
  38. {  
  39.     Texture = (g_TextureDiffuse);  
  40.     MinFilter = Linear;  
  41.     MagFilter = Linear;  
  42.     MipFilter = Linear;  
  43. };  
  44.   
  45. sampler g_SamplerCubeNormalization = sampler_state  
  46. {  
  47.     Texture = (g_TextureCubeNormalization);  
  48.     MinFilter = Linear;  
  49.     MagFilter = Linear;  
  50.     MipFilter = None;  
  51.     AddressU  = Clamp;  
  52.     AddressV  = Clamp;  
  53. };  
  54.   
  55. sampler g_SamplerSrcPosition = sampler_state  
  56. {  
  57.     Texture = (g_TextureSrcPosition);  
  58.     MinFilter = Point;  
  59.     MagFilter = Point;  
  60.     MipFilter = None;  
  61.     AddressU  = Clamp;  
  62.     AddressV  = Clamp;   
  63. };  
  64.   
  65. sampler g_SamplerSrcNormal = sampler_state  
  66. {  
  67.     Texture = (g_TextureSrcNormal);  
  68.     MinFilter = Point;  
  69.     MagFilter = Point;  
  70.     MipFilter = None;  
  71.     AddressU  = Clamp;  
  72.     AddressV  = Clamp;  
  73. };  
  74.   
  75. sampler g_SamplerSrcColor = sampler_state  
  76. {  
  77.     Texture = (g_TextureSrcColor);  
  78.     MinFilter = Point;  
  79.     MagFilter = Point;  
  80.     MipFilter = None;  
  81.     AddressU  = Clamp;  
  82.     AddressV  = Clamp;  
  83. };  
  84.   
  85. sampler g_SamplerSrcMaterial = sampler_state  
  86. {  
  87.     Texture = (g_TextureSrcMaterial);  
  88.     MinFilter = Point;  
  89.     MagFilter = Point;  
  90.     MipFilter = None;  
  91.     AddressU  = Clamp;  
  92.     AddressV  = Clamp;  
  93. };  
  94.   
  95. // ---------------------------------------------------------------  
  96. // Utitlities  
  97. // ---------------------------------------------------------------  
  98.   
  99. float3 cubeNormalize(float3 normal)  
  100. {  
  101.     return texCUBE(g_SamplerCubeNormalization, normal) * 2 - 1;  
  102. }  
  103.   
  104. half getAttenuation(half3 lightVec)  
  105. {  
  106.     return saturate(1 - dot(lightVec, lightVec) * g_OneOverSqrLightRadius);  
  107. }  
  108.   
  109. float3 F32_3xI8_Compress(float f)  
  110. {  
  111.     float u     = floor(f * 256.0);  
  112.     float res_u = f * 256.0 - u;  
  113.     float v     = floor(res_u * 256.0);  
  114.     float res_v = res_u * 256.0 - v;  
  115.     float w     = floor(res_v * 256.0);  
  116.       
  117.     return (1 / 256.0 * float3(u, v, w));  
  118. }  
  119.   
  120. float F32_3xI8_Decompress(float3 vec)  
  121. {  
  122.     return (vec.x + vec.y * 1.0 / 256.0 + vec.z * 1.0 / 65536.0f);  
  123. }  
  124.   
  125. // ---------------------------------------------------------------  
  126. // Geometry rendering (to 4 RTs)  
  127. // ---------------------------------------------------------------  
  128.   
  129. float4 VS_Geometry(float4 pos : POSITION) : POSITION  
  130. {  
  131.     return mul(pos, g_matWorldViewProj);  
  132. }  
  133.   
  134. // ---------------------------------------------------------------  
  135. // Render to 4 Render Targets  
  136. // ---------------------------------------------------------------  
  137.   
  138. struct VSOUTPUT_RenderToRTs  
  139. {  
  140.     float4 pos      : POSITION;  
  141.     float4 worldPos : TEXCOORD0;  
  142.     float2 texCoord : TEXCOORD1;  
  143.     float3 normal   : TEXCOORD2;  
  144. };  
  145.   
  146. struct PSOUTPUT_RenderToRTs  
  147. {  
  148.     float4 color    : COLOR0;  
  149.     float4 position : COLOR1;  
  150.     float4 normal   : COLOR2;  
  151.     float4 material : COLOR3;  
  152. };  
  153.   
  154. VSOUTPUT_RenderToRTs VS_RenderToRTs(float4 pos : POSITION, float3 normal : NORMAL, float2 texCoord : TEXCOORD0)  
  155. {  
  156.     VSOUTPUT_RenderToRTs vsRenderToRTs = (VSOUTPUT_RenderToRTs)0;  
  157.       
  158.     vsRenderToRTs.pos      = mul(pos, g_matWorldViewProj);  
  159.     vsRenderToRTs.worldPos = vsRenderToRTs.pos;  
  160.     vsRenderToRTs.texCoord = texCoord;  
  161.     vsRenderToRTs.normal   = mul(normal, (float3x3)g_matWorldView);  
  162.       
  163.     return vsRenderToRTs;  
  164. }  
  165.   
  166. PSOUTPUT_RenderToRTs PS_RenderToRTs(VSOUTPUT_RenderToRTs vsRenderToRTs)  
  167. {  
  168.     PSOUTPUT_RenderToRTs psRenderToRTs = (PSOUTPUT_RenderToRTs)0;  
  169.       
  170.     psRenderToRTs.color    = tex2D(g_SamplerDiffuse, vsRenderToRTs.texCoord);  
  171.     psRenderToRTs.position = float4(F32_3xI8_Compress(vsRenderToRTs.worldPos.z / vsRenderToRTs.worldPos.w), 1);  
  172.     psRenderToRTs.normal   = float4((normalize(vsRenderToRTs.normal) + 1) * 0.5, 0);  
  173.     psRenderToRTs.material = float4(g_MaterialAmbient, g_MaterialDiffuse, g_MaterialSpecular, g_MaterialShininess / 255);  
  174.       
  175.     return psRenderToRTs;  
  176. }  
  177.   
  178. // ---------------------------------------------------------------  
  179. // Ambient lighting  
  180. // ---------------------------------------------------------------  
  181.   
  182. struct VSOUTPUT_Ambient  
  183. {  
  184.     float4 pos : POSITION;  
  185.     float2 tex : TEXCOORD0;  
  186. };  
  187.   
  188. VSOUTPUT_Ambient VS_Ambient(float4 pos : POSITION, float2 tex : TEXCOORD0)  
  189. {  
  190.     VSOUTPUT_Ambient vsAmbient = (VSOUTPUT_Ambient)0;  
  191.       
  192.     vsAmbient.pos = mul(pos, g_matWorldViewProj);  
  193.     vsAmbient.tex = tex;  
  194.       
  195.     return vsAmbient;  
  196. }  
  197.   
  198. float4 PS_Ambient(VSOUTPUT_Ambient vsAmbient) : COLOR0  
  199. {  
  200.     return tex2D(g_SamplerSrcColor, vsAmbient.tex) * g_LightAmbient;  
  201. }  
  202.   
  203. // ---------------------------------------------------------------  
  204. // Diffuse lighting  
  205. // ---------------------------------------------------------------  
  206.   
  207. struct VSOUTPUT_Diffuse  
  208. {  
  209.     float4 pos     : POSITION;  
  210.     float4 pos2    : TEXCOORD0;  
  211.     float4 posProj : TEXCOORD1;  
  212. };  
  213.   
  214. VSOUTPUT_Diffuse VS_Diffuse(float4 inPos : POSITION)  
  215. {  
  216.     VSOUTPUT_Diffuse Out = (VSOUTPUT_Diffuse)0;  
  217.     float4 pos = mul(inPos, g_matWorldViewProj);  
  218.       
  219.     Out.pos     = pos;  
  220.     Out.pos2    = pos;  
  221.     Out.posProj = mul(pos, g_matTextureProj);  
  222.       
  223.     return Out;  
  224. }  
  225.   
  226. float4 PS_Diffuse(VSOUTPUT_Diffuse In) : COLOR0  
  227. {  
  228.     // Get depth, normal (unbias), color and material from 4 render targets  
  229.     // Use projected texture lookups  
  230.     half depth     = F32_3xI8_Decompress(tex2Dproj(g_SamplerSrcPosition, In.posProj));  
  231.     half4 normal   = tex2Dproj(g_SamplerSrcNormal, In.posProj) * 2 - 1;  
  232.     half4 color    = tex2Dproj(g_SamplerSrcColor, In.posProj);  
  233.     half4 material = tex2Dproj(g_SamplerSrcMaterial, In.posProj);  
  234.       
  235.     // Reconstruct original world space position  
  236.     half4 position = mul(half4(In.pos2.xy / In.pos2.w, depth, 1), g_matProjInvert);  
  237.     position.xyz /= position.w;  
  238.   
  239.     // Calculate diffuse component  
  240.     half3 lightVec = g_LightPositionViewSpace - position;  
  241.     half3 lightDir = normalize(lightVec);  
  242.     half diff = saturate(dot(normal, lightDir)) * material.y;  
  243.       
  244.     half attenuation = getAttenuation(lightVec);  
  245.   
  246.     return g_LightColor * color * diff * attenuation;  
  247. }  
  248.   
  249. //--------------------------------------------------------------------------------------  
  250. // Techniques  
  251. //--------------------------------------------------------------------------------------  
  252.   
  253. technique techRenderDeferred  
  254. {  
  255.     pass GeometryPass  
  256.     {  
  257.         VertexShader = compile vs_1_1 VS_Geometry();  
  258.         PixelShader  = null;  
  259.           
  260.         ZEnable          = true;  
  261.         ZWriteEnable     = true;  
  262.         StencilEnable    = false;  
  263.         AlphaBlendEnable = false;  
  264.         ColorWriteEnable = Alpha;  
  265.     }  
  266.       
  267.     pass RenderToRTsPass  
  268.     {  
  269.         VertexShader = compile vs_2_0 VS_RenderToRTs();  
  270.         PixelShader  = compile ps_2_0 PS_RenderToRTs();  
  271.           
  272.         ZWriteEnable = false;  
  273.         ColorWriteEnable = 0x0000000F; // Red | Green | Blue | Alpha  
  274.     }  
  275.       
  276.     pass AmbientPass  
  277.     {  
  278.         VertexShader = compile vs_2_0 VS_Ambient();  
  279.         PixelShader  = compile ps_2_0 PS_Ambient();  
  280.           
  281.         ZWriteEnable = false;  
  282.         ZEnable = false;  
  283.           
  284.         AlphaBlendEnable = true;  
  285.         SrcBlend  = One;  
  286.         DestBlend = One;  
  287.     }  
  288.       
  289.     pass DiffusePass  
  290.     {  
  291.         VertexShader = compile vs_1_1 VS_Diffuse();  
  292.         PixelShader  = compile ps_2_0 PS_Diffuse();  
  293.           
  294.         ZWriteEnable = false;  
  295.         ZEnable = false;  
  296.           
  297.         AlphaBlendEnable = true;  
  298.         SrcBlend  = One;  
  299.         DestBlend = One;  
  300.           
  301.         CullMode = CW;  
  302.           
  303.         ColorWriteEnable = 0x0000000F;  
  304.     }  
  305. }  

使用该Shader实现的延迟效果图如下所示:



总结:

游戏的精髓是图形学算法,作为游戏开发者对数学算法必须要掌握,对一些后处理算法要了解其原理。很多开发者认为自己写写逻辑就可以了,其实写逻辑也会遇到算法接口的调用,在使用接口时自己也要明白算法的原理,这样对于提升自己的技能非常有帮助。


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论