【浅墨Unity3D Shader编程】之九 深入理解Unity5中的Standard Shader (一)&屏幕水幕特效的实现



本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

文章链接: http://blog.csdn.net/poem_qianmo/article/details/49556461

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

本文工程使用的Unity3D版本: 5.2.1 


 

 

概要:本文主要介绍了Unity5中的标准着色器,并且也涉及到了基于物理的着色、延迟渲染等高级着色技术,而在文章后半部分,也对屏幕水幕特效的实现方法进行了讲解与分析。


 

依然是附上一组本文配套工程的运行截图之后,便开始我们的正文。如下图。

 

打开水幕特效的效果图:

 

 

原始的城镇场景:

 

 

需要说明,这里的水幕特效是动态的水流效果。本来准备传GIF上来展示动态的效果,但受图片尺寸2M的限制,无法出色地表现出动态效果(帧数有限,图片清晰度也会大打折扣)。建议感兴趣的朋友们下载这里提供的游戏场景的exe,自己在机器上体验水幕效果,比看博文中贴出的图片效果好太多。

 

【可运行的本文配套exe游戏场景请点击这里下载】

 

提示:在此游戏场景中按F键可以开关屏幕特效。

 

在文章末尾有更多的运行截图,并提供了源工程的下载。

 

好的,正文开始。

 

 

 



一、认识Unity5中的Standard Shader

 



Unity5中重点推出了一套基于物理的着色(Physically Based Shading,PBS)的多功能Shader,叫做标准着色器(Standard Shader)。这套Shader的设计初衷是化繁为简。想用这样的一个多功能Shader,来代替之前多种多样的Shader各司其职,对于不同的材质效果,需要不同的Shader的局面。

 Unity 5中目前有两个标准着色器,一个名为Standard,我们称它为标准着色器的标准版,另一个名为Standard(Specular Setup),我们称它为标准着色器的高光版,它们共同组成了一个完整的PBS光照明模型,且非常易于使用。其实这两个Shader基本差不多,只是有细微的属性参数上的区别。标准版这边的_Metallic(金属性)、_MetallicGlossMap(金属光泽贴图),被高光版的_SpecColor(高光颜色)、_SpecGlossMap(高光颜色法线贴图)所代替。

标准着色器主要是针对硬质表面(也就是建筑材质)而设计的,可以处理大多数现实世界的材质,例如石头、陶瓷、铜器、银器或橡胶等。同时,它也可以非常出色地处理一些非硬质表面的材质,例如皮肤、头发或布料等。

 

如下的一个Unity官方放出的名为VikingVillage的场景中,所有的物体材质都是使用的标准着色器,标准着色器的广泛适用性可见一斑。

 

 

而在Unity5的官方放出的演示demo视频中,也是用一个标准着色器,Standard(Specular Setup),就完成了游戏场景中绝大多数物体的材质显示。

Unity5 Standard Shader的演示Demo可以看这里:

http://www.iqiyi.com/w_19rquxac31.html


 Unity5 PBS技术的演示Demo截图:




下面稍微对Standard shader中核心的概念——基于物理的着色做一个大概的了解。



 


1.1 基于物理的着色(Physically Based Shading)技术概览

 

 


基于物理的着色(Physically Based Shading,简称PBS)就是以某种方式模拟现实中材质和光照的相互作用的一种着色方法。这种方法在需要光照和材质更加直观和逼真地协同工作的场合下优势非常明显。基于物理的着色模拟了光线在现实中的行为,实现了在不同的光照条件下的逼真效果。为实现这种效果需要遵循各种物理原理,包括能量守恒(也就是物体反射出去的光量不可能超过所接收的光量),Fresnel反射(所有表面反射在掠射角(grazing angles)处更加强烈),以及物体表面是如何自我遮挡等原理。

其实,PBS技术在多年前于虚幻三游戏引擎中就已经被广泛使用,而Unity直到最近刚发布的第五代,才将此特性作为官方的特性实现出来。而在Unity5发布之前,Unity的Asset Store中,已经有不少的第三方插件,在Unity中实现了PBS技术。

 

关于PBS技术,知乎上有一个专栏,专门介绍PBS技术的一些相关原理,在这里推荐一下:

 

基于物理着色(一)

基于物理着色(二)- Microfacet材质和多层材质

基于物理着色(三)- Disney和UE4的实现


这边贴一张专栏中的配套渲染图,效果非常出色:


 

 


 

1.2 如何使用标准着色器

 


可以在Unity5中任意材质的Shader选项中的最前面看到两个Standard Shader字样的选项。其中第一个是普通版,第二个带Specularsetup是高光版。

 


标准着色器标准版材质界面:


 

标准着色器的高光版材质界面:



 

不难得知,标准着色器引入了新的材质编辑器,它使PBS的材质编辑工作比以前的非PBS材质更简单。新的编辑器不但简洁,还提供了材质的所有可能用到的选项。在新编辑器中,我们不需要选用不同的着色器来改变纹理通道;不会再出现 “texture unused, please choose another shader” 这样的提示;也不再需要通过切换着色器来改变混合模式。

所有的纹理通道都是备选的,无需强制使用,任何一个闲置通道的相关代码都会在编译时被优化掉,因此完全不用担心效率方面的问题。unity会根据我们输入到编辑器中的数据来生成正确的代码,并使整个过程保持高效。

 

另外,我们可以用ctrl+点击纹理的方式预览大图,并且还可以分别查看颜色和Alpha通道。

ctrl+点击纹理,得到的纹理放大的预览:


 

OK,关于Unity5中的StandardShader的概念,大概先就讲这么多。更多细节可以参考Unity官方文档:

http://docs.unity3d.com/Manual/shader-StandardShader.html

 

以及这里Unity5 Standard Shader的官方文档论坛翻译版:

http://forum.china.unity3d.com/thread-897-1-1.html

 

 

 

 

1.3 理解标准着色器的组成


上面说了很多标准着色器的强大之处,其实,作为一个着色器,它也没有什么神秘的地方,无非就是两个Shader源文件,加上一堆CG头文件组成的两个功能稍微复杂全面一些的Shader而已。其中,两个Shader源文件里,又按渲染路径分为了很多的SubShader,每个SubShader里面又分为了很多Pass。而CG文件中,主要包含了Shader的支持函数,相关的宏等为Shader源文件提供支持的代码。

 

Unity5中标准着色器的组成,归纳概括如下:

 

  • 两个Shader源文件
  • 七个CG头文件
  • 一个脚本文件(用于自定义材质编辑器UI)

 

下面分别对每个文件进行一个简单的介绍。

 

1)两个Shader源文件


  • Stardard.shader着色器源文件 - 标准着色器的标准版
  • StardardSpecular.shader着色器源文件 - 标准着色器的高光版

 

2)七个CG头文件


  • UnityStandardBRDF.cginc头文件-用于存放标准着色器处理BRDF材质属性相关的函数与宏
  • UnityStandardConfig.cginc头文件-用于存放标准着色器配置相关的代码(其实里面就几个宏)
  • UnityStandardCore.cginc头文件-用于存放标准着色器的主要代码(如顶点着色函数、片段着色函数等相关函数)
  • UnityStandardInput.cginc头文件-用于存放标准着色器输入结构相关的工具函数与宏
  • UnityStandardMeta.cginc头文件-用于存放标准着色器meta通道中会用到的工具函数与宏
  • UnityStandardShadow.cginc头文件-用于存放标准着色器阴影贴图采样相关的工具函数与宏
  • UnityStandardUtils.cginc头文件-用于存放标准着色器共用的一些工具函数

  

在包括本文在内的接下来的几次更新中,本系列文章将试着花篇幅来剖析这两个Shader源文件,和依附的几个CG头文件的源码,从而一窥Unity5中新版Shader书写体系的究竟。

 

 

3)一个脚本文件


  • StandardShaderGUI.cs脚本文件——定义了特定的自定义编辑器UI界面



标准着色器对应材质的编辑器外观不同于一般的Shader,就是因为在Shader末尾书写了如下的代码: 

 

[csharp]  view plain  copy
 print ?
  1. //使用特定的自定义编辑器UI界面  
  2. CustomEditor "StandardShaderGUI"  


 

标准着色器对应材质的编辑器外观如下:

 

 

 

一般的着色器对应材质的编辑器外观如下:


 

 

 

 



二、Unity5标准着色器源代码剖析之一:架构分析篇




上文已经提到过,标准着色器源代码的剖析是一个小小的马拉松,完全解析起来篇幅会很长,所以本系列文章将对剖析的过程进行连载,此节为连载的第一部分。

 

在这里先贴出经过浅墨详细注释的标准着色器标准版的源代码,并对架构进行简单的分析,而对每个通道的剖析在稍后的更新中会进行。

 

[cpp]  view plain  copy
 print ?
  1. //-----------------------------------------------【Shader说明】---------------------------------------------------  
  2. //       Unity5.2.1 Built-in Standard Shader  
  3. //      2015年10月  Commented by  浅墨      
  4. //      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo  
  5. //---------------------------------------------------------------------------------------------------------------------  
  6.   
  7. Shader "Standard"  
  8. {  
  9.     //------------------------------------【属性值】------------------------------------  
  10.     Properties  
  11.     {  
  12.         //主颜色  
  13.         _Color("Color", Color) = (1,1,1,1)  
  14.         //主纹理  
  15.         _MainTex("Albedo", 2D) = "white" {}  
  16.   
  17.         //Alpha剔除值  
  18.         _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5  
  19.         //平滑、光泽度  
  20.         _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5  
  21.   
  22.         //金属性  
  23.         [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0  
  24.         //金属光泽纹理图  
  25.         _MetallicGlossMap("Metallic", 2D) = "white" {}  
  26.   
  27.         //凹凸的尺度  
  28.         _BumpScale("Scale", Float) = 1.0  
  29.         //法线贴图  
  30.         _BumpMap("Normal Map", 2D) = "bump" {}  
  31.   
  32.         //高度缩放尺度  
  33.         _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02  
  34.         //高度纹理图  
  35.         _ParallaxMap ("Height Map", 2D) = "black" {}  
  36.   
  37.         //遮挡强度  
  38.         _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0  
  39.         //遮挡纹理图  
  40.         _OcclusionMap("Occlusion", 2D) = "white" {}  
  41.   
  42.         //自发光颜色  
  43.         _EmissionColor("Color", Color) = (0,0,0)  
  44.         //自发光纹理图  
  45.         _EmissionMap("Emission", 2D) = "white" {}  
  46.           
  47.         //细节掩膜图  
  48.         _DetailMask("Detail Mask", 2D) = "white" {}  
  49.   
  50.         //细节纹理图  
  51.         _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}  
  52.         //细节法线贴图尺度  
  53.         _DetailNormalMapScale("Scale", Float) = 1.0  
  54.         //细节法线贴图  
  55.         _DetailNormalMap("Normal Map", 2D) = "bump" {}  
  56.   
  57.         //二级纹理的UV设置  
  58.         [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0  
  59.   
  60.         //混合状态的定义  
  61.         [HideInInspector] _Mode ("__mode", Float) = 0.0  
  62.         [HideInInspector] _SrcBlend ("__src", Float) = 1.0  
  63.         [HideInInspector] _DstBlend ("__dst", Float) = 0.0  
  64.         [HideInInspector] _ZWrite ("__zw", Float) = 1.0  
  65.     }  
  66.   
  67.     //===========开始CG着色器语言编写模块===========  
  68.     CGINCLUDE  
  69.         //BRDF相关的一个宏  
  70.         #define UNITY_SETUP_BRDF_INPUT MetallicSetup  
  71.     //===========结束CG着色器语言编写模块===========  
  72.     ENDCG  
  73.   
  74.   
  75.     //------------------------------------【子着色器1】------------------------------------  
  76.     // 此子着色器用于Shader Model 3.0  
  77.     //----------------------------------------------------------------------------------------  
  78.     SubShader  
  79.     {  
  80.         //渲染类型设置:不透明  
  81.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }  
  82.   
  83.         //细节层次设为:300  
  84.         LOD 300  
  85.           
  86.         //--------------------------------通道1-------------------------------  
  87.         // 正向基础渲染通道(Base forward pass)  
  88.         // 处理方向光,自发光,光照贴图等 ...  
  89.         Pass  
  90.         {  
  91.             //设置通道名称  
  92.             Name "FORWARD"   
  93.   
  94.             //于通道标签中设置光照模型为ForwardBase,正向渲染基础通道  
  95.             Tags { "LightMode" = "ForwardBase" }  
  96.   
  97.             //混合操作:源混合乘以目标混合  
  98.             Blend [_SrcBlend] [_DstBlend]  
  99.             // 根据_ZWrite参数,设置深度写入模式开关与否  
  100.             ZWrite [_ZWrite]  
  101.   
  102.             //===========开启CG着色器语言编写模块===========  
  103.             CGPROGRAM  
  104.   
  105.             //着色器编译目标:Model 3.0  
  106.             #pragma target 3.0  
  107.   
  108.             //编译指令:不使用GLES渲染器编译  
  109.             #pragma exclude_renderers gles  
  110.               
  111.             // ---------编译指令:着色器编译多样化--------  
  112.             #pragma shader_feature _NORMALMAP  
  113.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  114.             #pragma shader_feature _EMISSION  
  115.             #pragma shader_feature _METALLICGLOSSMAP   
  116.             #pragma shader_feature ___ _DETAIL_MULX2  
  117.             #pragma shader_feature _PARALLAXMAP  
  118.               
  119.             //--------着色器编译多样化快捷指令------------  
  120.             //编译指令:编译正向渲染基础通道(用于正向渲染中,应用环境光照、主方向光照和顶点/球面调和光照)所需的所有变体。  
  121.             //这些变体用于处理不同的光照贴图类型、主要方向光源的阴影选项的开关与否  
  122.             #pragma multi_compile_fwdbase  
  123.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  124.             #pragma multi_compile_fog  
  125.               
  126.             //编译指令:告知编译器顶点和片段着色函数的名称  
  127.             #pragma vertex vertForwardBase  
  128.             #pragma fragment fragForwardBase  
  129.   
  130.             //包含辅助CG头文件  
  131.             #include "UnityStandardCore.cginc"  
  132.   
  133.             //===========结束CG着色器语言编写模块===========  
  134.             ENDCG  
  135.         }  
  136.         //--------------------------------通道2-------------------------------  
  137.         // 正向附加渲染通道(Additive forward pass)  
  138.         // 以每个光照一个通道的方式应用附加的逐像素光照  
  139.         Pass  
  140.         {  
  141.             //设置通道名称  
  142.             Name "FORWARD_DELTA"  
  143.   
  144.             //于通道标签中设置光照模型为ForwardAdd,正向渲染附加通道  
  145.             Tags { "LightMode" = "ForwardAdd" }  
  146.   
  147.             //混合操作:源混合乘以1  
  148.             Blend [_SrcBlend] One  
  149.   
  150.             //附加通道中的雾效应该为黑色  
  151.             Fog { Color (0,0,0,0) }   
  152.   
  153.             //关闭深度写入模式  
  154.             ZWrite Off  
  155.             //设置深度测试模式:小于等于  
  156.             ZTest LEqual  
  157.   
  158.             //===========开启CG着色器语言编写模块===========  
  159.             CGPROGRAM  
  160.   
  161.             //着色器编译目标:Model 3.0  
  162.             #pragma target 3.0  
  163.             //编译指令:不使用GLES渲染器编译  
  164.             #pragma exclude_renderers gles  
  165.   
  166.             // ---------编译指令:着色器编译多样化--------  
  167.             #pragma shader_feature _NORMALMAP  
  168.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  169.             #pragma shader_feature _METALLICGLOSSMAP  
  170.             #pragma shader_feature ___ _DETAIL_MULX2  
  171.             #pragma shader_feature _PARALLAXMAP  
  172.               
  173.             //--------使用Unity内置的着色器编译多样化快捷指令------------  
  174.             //编译指令:编译正向渲染基础通道所需的所有变体,但同时为上述通道的处理赋予了光照实时阴影的能力。  
  175.             #pragma multi_compile_fwdadd_fullshadows  
  176.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  177.             #pragma multi_compile_fog  
  178.   
  179.             //编译指令:告知编译器顶点和片段着色函数的名称  
  180.             #pragma vertex vertForwardAdd  
  181.             #pragma fragment fragForwardAdd  
  182.   
  183.             //包含辅助CG头文件  
  184.             #include "UnityStandardCore.cginc"  
  185.   
  186.             //===========结束CG着色器语言编写模块===========  
  187.             ENDCG  
  188.         }  
  189.   
  190.         // --------------------------------通道3-------------------------------  
  191.         //  阴影渲染通道(Shadow Caster pass)  
  192.         //  将将物体的深度渲染到阴影贴图或深度纹理中  
  193.         Pass   
  194.         {  
  195.             //设置通道名称  
  196.             Name "ShadowCaster"  
  197.             //于通道标签中设置光照模型为ShadowCaster。  
  198.             //此光照模型代表着将物体的深度渲染到阴影贴图或深度纹理。  
  199.             Tags { "LightMode" = "ShadowCaster" }  
  200.   
  201.             //开启深入写入模式  
  202.             ZWrite On   
  203.             //设置深度测试模式:小于等于  
  204.             ZTest LEqual  
  205.   
  206.             //===========开启CG着色器语言编写模块===========  
  207.             CGPROGRAM  
  208.   
  209.             //着色器编译目标:Model 3.0  
  210.             #pragma target 3.0  
  211.   
  212.             //编译指令:不使用GLES渲染器编译  
  213.             #pragma exclude_renderers gles  
  214.               
  215.   
  216.             // ---------编译指令:着色器编译多样化--------  
  217.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  218.               
  219.             //--------着色器编译多样化快捷指令------------  
  220.             //进行阴影投射相关的多着色器变体的编译  
  221.             #pragma multi_compile_shadowcaster  
  222.   
  223.             //编译指令:告知编译器顶点和片段着色函数的名称  
  224.             #pragma vertex vertShadowCaster  
  225.             #pragma fragment fragShadowCaster  
  226.               
  227.             //包含辅助CG头文件  
  228.             #include "UnityStandardShadow.cginc"  
  229.   
  230.             //===========结束CG着色器语言编写模块===========  
  231.             ENDCG  
  232.         }  
  233.   
  234.         // --------------------------------通道4-------------------------------  
  235.         //  延迟渲染通道(Deferred Render Pass)  
  236.         Pass  
  237.         {  
  238.             //设置通道名称  
  239.             Name "DEFERRED"  
  240.             //于通道标签中设置光照模型为Deferred,延迟渲染通道  
  241.             Tags { "LightMode" = "Deferred" }  
  242.   
  243.             CGPROGRAM  
  244.             #pragma target 3.0  
  245.             // TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT  
  246.             #pragma exclude_renderers nomrt gles  
  247.               
  248.   
  249.             //---------编译指令:着色器编译多样化(shader_feature)--------  
  250.             #pragma shader_feature _NORMALMAP  
  251.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  252.             #pragma shader_feature _EMISSION  
  253.             #pragma shader_feature _METALLICGLOSSMAP  
  254.             #pragma shader_feature ___ _DETAIL_MULX2  
  255.             #pragma shader_feature _PARALLAXMAP  
  256.   
  257.             //---------编译指令:着色器编译多样化(multi_compile)--------  
  258.             #pragma multi_compile ___ UNITY_HDR_ON  
  259.             #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON  
  260.             #pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE  
  261.             #pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON  
  262.               
  263.             //编译指令:告知编译器顶点和片段着色函数的名称  
  264.             #pragma vertex vertDeferred  
  265.             #pragma fragment fragDeferred  
  266.   
  267.             //包含辅助CG头文件  
  268.             #include "UnityStandardCore.cginc"  
  269.   
  270.             //===========结束CG着色器语言编写模块===========  
  271.             ENDCG  
  272.         }  
  273.   
  274.         // --------------------------------通道5-------------------------------  
  275.         //元通道(Meta Pass)  
  276.         //为全局光照(GI),光照贴图等技术提取相关参数,如(emission, albedo等参数值)  
  277.         //此通道并不在常规的渲染过程中使用  
  278.         Pass  
  279.         {  
  280.             //设置通道名称  
  281.             Name "META"   
  282.   
  283.             //于通道标签中设置光照模型为Meta  
  284.             //(截止2015年10月22日,Unity 5.2.1的官方文档中并没有收录此光照模型,应该是Unity官方的疏漏)  
  285.             Tags { "LightMode"="Meta" }  
  286.             //关闭剔除操作  
  287.             Cull Off  
  288.   
  289.             //===========开启CG着色器语言编写模块===========  
  290.             CGPROGRAM  
  291.   
  292.             //编译指令:告知编译器顶点和片段着色函数的名称  
  293.             #pragma vertex vert_meta  
  294.             #pragma fragment frag_meta  
  295.   
  296.             //---------编译指令:着色器编译多样化--------  
  297.             #pragma shader_feature _EMISSION  
  298.             #pragma shader_feature _METALLICGLOSSMAP  
  299.             #pragma shader_feature ___ _DETAIL_MULX2  
  300.   
  301.             //包含辅助CG头文件  
  302.             #include "UnityStandardMeta.cginc"  
  303.   
  304.             //===========结束CG着色器语言编写模块===========  
  305.             ENDCG  
  306.         }  
  307.     }  
  308.   
  309.     //------------------------------------【子着色器2】-----------------------------------  
  310.     // 此子着色器用于Shader Model 2.0  
  311.     //----------------------------------------------------------------------------------------  
  312.     SubShader  
  313.     {  
  314.         //渲染类型设置:不透明  
  315.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }  
  316.         //细节层次设为:150  
  317.         LOD 150  
  318.   
  319.         //--------------------------------通道1-------------------------------  
  320.         // 正向基础渲染通道(Base forward pass)  
  321.         // 处理方向光,自发光,光照贴图等 ...  
  322.         Pass  
  323.         {  
  324.             //设置通道名称  
  325.             Name "FORWARD"   
  326.             //于通道标签中设置光照模型为ForwardBase,正向渲染基础通道  
  327.             Tags { "LightMode" = "ForwardBase" }  
  328.             //混合操作:源混合乘以目标混合,即结果为两者的混合  
  329.             Blend [_SrcBlend] [_DstBlend]  
  330.             // 根据_ZWrite参数,设置深度写入模式开关与否  
  331.             ZWrite [_ZWrite]  
  332.   
  333.             //===========开启CG着色器语言编写模块===========  
  334.             CGPROGRAM  
  335.             //着色器编译目标:Model 2.0  
  336.             #pragma target 2.0  
  337.   
  338.             // ---------编译指令:着色器编译多样化--------  
  339.             #pragma shader_feature _NORMALMAP  
  340.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  341.             #pragma shader_feature _EMISSION   
  342.             #pragma shader_feature _METALLICGLOSSMAP   
  343.             #pragma shader_feature ___ _DETAIL_MULX2  
  344.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP  
  345.   
  346.             //跳过如下变体的编译,简化编译过程  
  347.             #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE  
  348.               
  349.             //--------着色器编译多样化快捷指令------------  
  350.             #pragma multi_compile_fwdbase  
  351.             #pragma multi_compile_fog  
  352.   
  353.             //编译指令:告知编译器顶点和片段着色函数的名称  
  354.             #pragma vertex vertForwardBase  
  355.             #pragma fragment fragForwardBase  
  356.   
  357.             //包含辅助CG头文件  
  358.             #include "UnityStandardCore.cginc"  
  359.   
  360.             //===========结束CG着色器语言编写模块===========  
  361.             ENDCG  
  362.         }  
  363.   
  364.         //--------------------------------通道2-------------------------------  
  365.         // 正向附加渲染通道(Additive forward pass)  
  366.         // 以每个光照一个通道的方式应用附加的逐像素光照  
  367.         Pass  
  368.         {  
  369.             //设置通道名称  
  370.             Name "FORWARD_DELTA"  
  371.   
  372.             //于通道标签中设置光照模型为ForwardAdd,正向渲染附加通道  
  373.             Tags { "LightMode" = "ForwardAdd" }  
  374.   
  375.             //混合操作:源混合乘以1  
  376.             Blend [_SrcBlend] One  
  377.   
  378.             //附加通道中的雾效应该为黑色  
  379.             Fog { Color (0,0,0,0) }   
  380.   
  381.             //关闭深度写入模式  
  382.             ZWrite Off  
  383.   
  384.             //设置深度测试模式:小于等于  
  385.             ZTest LEqual  
  386.   
  387.             //===========开启CG着色器语言编写模块===========  
  388.             CGPROGRAM  
  389.             //着色器编译目标:Model 2.0  
  390.             #pragma target 2.0  
  391.   
  392.             // ---------编译指令:着色器编译多样化--------  
  393.             #pragma shader_feature _NORMALMAP  
  394.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  395.             #pragma shader_feature _METALLICGLOSSMAP  
  396.             #pragma shader_feature ___ _DETAIL_MULX2  
  397.   
  398.             //跳过一些变体的编译  
  399.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP  
  400.             #pragma skip_variants SHADOWS_SOFT  
  401.   
  402.             //--------使用Unity内置的着色器编译多样化快捷指令------------  
  403.             //编译指令:编译正向渲染基础通道所需的所有变体,但同时为上述通道的处理赋予了光照实时阴影的能力。  
  404.             #pragma multi_compile_fwdadd_fullshadows  
  405.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  406.             #pragma multi_compile_fog  
  407.   
  408.             //编译指令:告知编译器顶点和片段着色函数的名称  
  409.             #pragma vertex vertForwardAdd  
  410.             #pragma fragment fragForwardAdd  
  411.   
  412.             //包含辅助CG头文件  
  413.             #include "UnityStandardCore.cginc"  
  414.   
  415.             //===========结束CG着色器语言编写模块===========  
  416.             ENDCG  
  417.         }  
  418.   
  419.         // --------------------------------通道3-------------------------------  
  420.         //  阴影渲染通道(Shadow Caster pass)  
  421.         //  将将物体的深度渲染到阴影贴图或深度纹理中  
  422.         Pass   
  423.         {  
  424.             //设置通道名称  
  425.             Name "ShadowCaster"  
  426.   
  427.             //于通道标签中设置光照模型为ShadowCaster。  
  428.             //此光照模型代表着将物体的深度渲染到阴影贴图或深度纹理。  
  429.             Tags { "LightMode" = "ShadowCaster" }  
  430.   
  431.             //开启深入写入模式  
  432.             ZWrite On  
  433.   
  434.             //设置深度测试模式:小于等于  
  435.             ZTest LEqual  
  436.   
  437.             //===========开启CG着色器语言编写模块===========  
  438.             CGPROGRAM  
  439.             //着色器编译目标:Model 2.0  
  440.             #pragma target 2.0  
  441.   
  442.             //---------编译指令:着色器编译多样化(shader_feature)--------  
  443.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  444.               
  445.             //编译指令:跳过某些变体的编译  
  446.             #pragma skip_variants SHADOWS_SOFT  
  447.   
  448.             //快捷编译指令:进行阴影投射相关的多着色器变体的编译  
  449.             #pragma multi_compile_shadowcaster  
  450.   
  451.             //编译指令:告知编译器顶点和片段着色函数的名称  
  452.             #pragma vertex vertShadowCaster  
  453.             #pragma fragment fragShadowCaster  
  454.   
  455.             //包含辅助CG头文件  
  456.             #include "UnityStandardShadow.cginc"  
  457.   
  458.             //===========结束CG着色器语言编写模块===========  
  459.             ENDCG  
  460.         }  
  461.   
  462.         // --------------------------------通道4-------------------------------  
  463.         //元通道(Meta Pass)  
  464.         //为全局光照(GI),光照贴图等技术提取相关参数,如(emission, albedo等参数值)  
  465.         //此通道并不在常规的渲染过程中使用  
  466.         Pass  
  467.         {  
  468.             //设置通道名称  
  469.             Name "META"   
  470.   
  471.             //于通道标签中设置光照模型为Meta  
  472.             //(截止2015年10月22日,Unity 5.2.1的官方文档中并没有收录此光照模型,应该是Unity官方的疏漏)  
  473.             Tags { "LightMode"="Meta" }  
  474.             //关闭剔除操作  
  475.             Cull Off  
  476.   
  477.             //===========开启CG着色器语言编写模块===========  
  478.             CGPROGRAM  
  479.   
  480.             //编译指令:告知编译器顶点和片段着色函数的名称  
  481.             #pragma vertex vert_meta  
  482.             #pragma fragment frag_meta  
  483.   
  484.             //---------编译指令:着色器编译多样化--------  
  485.             #pragma shader_feature _EMISSION  
  486.             #pragma shader_feature _METALLICGLOSSMAP  
  487.             #pragma shader_feature ___ _DETAIL_MULX2  
  488.   
  489.             //包含辅助CG头文件  
  490.             #include "UnityStandardMeta.cginc"  
  491.   
  492.             //===========结束CG着色器语言编写模块===========  
  493.             ENDCG  
  494.         }  
  495.     }  
  496.   
  497.     //回退Shader为顶点光照Shader  
  498.     FallBack "VertexLit"  
  499.     //使用特定的自定义编辑器UI界面  
  500.     CustomEditor "StandardShaderGUI"  
  501. }  

 

标准着色器的源代码部分相对于着色器的体量来说,算是有点长的,加上注释后有近500行。先稍微把它的架构稍微理一下。

标准着色器由两个SubShader组成。第一个SubShader用于处理Shader Model 3.0,有5个通道,第二个SubShader用于处理Shader Model 2.0, 有4个通道,

详细的架构如下图:

 


OK,标准着色器的架构大致如上。今天就先讲这么多,上面代码的稍微有些看不懂没关系,从下次更新开始,就将深入到每个Pass的顶点和片段着色器函数之中,逐行注释与分析他们的指令细节。

 

而不难发现,标准着色器代码的第一个SubShader比第二个SubShader多出了一个延迟渲染通道(Deferred Render Pass),下面稍微提一下延迟渲染的基本概念。

 

 

 



 

三、关于延迟渲染(Deferred Render)





看过《GPU Gems2》的朋友们应该都有所了解,延迟渲染(Deferred Render,又称Deferred Shading)是次时代引擎必备的渲染方式之一。

需要注意,Deferred Render和Deferred Shading有一点细微的差别。

  • Deferred Shading:将所有的Shading全部转到Deferred阶段进行。
  • Deferred Render:只是有选择地将Lighting转到Deferred阶段进行。

Unity中默认使用的是Deferred Shading。

 

而延迟渲染,一言以蔽之,就是将光照/渲染的计算延迟到第二步进行,避免多次渲染同一个像素,从而减少多余的计算操作,以提高渲染效率的一种先进的渲染方式。

 

延迟渲染最大的优势是可以实现同屏中数量众多的动态光源(十几到几十个),这在传统的渲染管线中是很难实现的。

 

更多延迟渲染的细节,这边不细说,只是提供一些链接,以作进一步了解延迟渲染的导论之用:


  • https://zh.wikipedia.org/wiki/%E5%BB%B6%E6%9C%9F%E7%9D%80%E8%89%B2 
  • http://blog.csdn.net/noslopforever/article/details/3951273 
  • http://blog.sina.com.cn/s/blog_458f871201017i06.html 
  • http://www.cnblogs.com/lancidie/archive/2011/08/18/2144748.html 
  • http://blog.csdn.net/pizi0475/article/details/7932920
  • http://www.cnblogs.com/wangchengfeng/p/3440097.html
  • http://blog.csdn.net/bugrunner/article/details/7436600

 

 

 

 


四、屏幕水幕特效的实现

 

 


在上一篇文章中有提到过,Unity中的屏幕特效通常分为两部分来实现:

 

  • Shader实现部分
  • 脚本实现部分

 

下面依然是从这两个方面对本次的特效进行实现。

 

而在这之前,需要准备好一张水滴(水滴太多了也就成了水幕了)的效果图片(google“water drop”一下,稍微筛选一下就有了,最好是能找到或者自己加工成无缝衔接的),放置于我们特效的脚本实现文件目录附加的一个Resources的文件夹中,那么我们在脚本中适当的地方写上一句:

Texture2 = Resources.Load("ScreenWaterDrop")as Texture2D;

 

就可以读取到这张图片了。

浅墨准备的图片如下(无水印本图片的可以在浅墨的Github中找到,或者直接下文章末尾的项目工程)。

ScreenWaterDrop.png:


 

 


 

4.1 Shader实现部分

 


老规矩,先上详细注释的代码。

[cpp]  view plain  copy
 print ?
  1. //-----------------------------------------------【Shader脚本说明】---------------------------------------------------  
  2. //       屏幕水幕特效的实现代码-Shader脚本部分  
  3. //      2015年10月  Created by  浅墨  
  4. //      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo  
  5. //---------------------------------------------------------------------------------------------------------------------  
  6.   
  7. Shader "浅墨Shader编程/Volume9/ScreenWaterDropEffect"  
  8. {  
  9.     //------------------------------------【属性值】------------------------------------  
  10.     Properties  
  11.     {  
  12.         //主纹理  
  13.         _MainTex ("Base (RGB)", 2D) = "white" {}  
  14.         //屏幕水滴的素材图  
  15.         _ScreenWaterDropTex ("Base (RGB)", 2D) = "white" {}  
  16.         //当前时间  
  17.         _CurTime ("Time", Range(0.0, 1.0)) = 1.0  
  18.         //X坐标上的水滴尺寸  
  19.         _SizeX ("SizeX", Range(0.0, 1.0)) = 1.0  
  20.         //Y坐标上的水滴尺寸  
  21.         _SizeY ("SizeY", Range(0.0, 1.0)) = 1.0  
  22.         //水滴的流动速度  
  23.         _DropSpeed ("Speed", Range(0.0, 10.0)) = 1.0  
  24.         //溶解度  
  25.         _Distortion ("_Distortion", Range(0.0, 1.0)) = 0.87  
  26.     }  
  27.   
  28.     //------------------------------------【唯一的子着色器】------------------------------------  
  29.     SubShader  
  30.     {  
  31.         Pass  
  32.         {  
  33.             //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTest Off)  
  34.             ZTest Always  
  35.               
  36.             //===========开启CG着色器语言编写模块===========  
  37.             CGPROGRAM  
  38.   
  39.             //编译指令:告知编译器顶点和片段着色函数的名称  
  40.             #pragma vertex vert  
  41.             #pragma fragment frag  
  42.             #pragma fragmentoption ARB_precision_hint_fastest  
  43.             //编译指令: 指定着色器编译目标为Shader Model 3.0  
  44.             #pragma target 3.0  
  45.   
  46.             //包含辅助CG头文件  
  47.             #include "UnityCG.cginc"  
  48.   
  49.             //外部变量的声明  
  50.             uniform sampler2D _MainTex;  
  51.             uniform sampler2D _ScreenWaterDropTex;  
  52.             uniform float _CurTime;  
  53.             uniform float _DropSpeed;  
  54.             uniform float _SizeX;  
  55.             uniform float _SizeY;  
  56.             uniform float _Distortion;  
  57.             uniform float2 _MainTex_TexelSize;  
  58.               
  59.             //顶点输入结构  
  60.             struct vertexInput  
  61.             {  
  62.                 float4 vertex : POSITION;//顶点位置  
  63.                 float4 color : COLOR;//颜色值  
  64.                 float2 texcoord : TEXCOORD0;//一级纹理坐标  
  65.             };  
  66.   
  67.             //顶点输出结构  
  68.             struct vertexOutput  
  69.             {  
  70.                 half2 texcoord : TEXCOORD0;//一级纹理坐标  
  71.                 float4 vertex : SV_POSITION;//像素位置  
  72.                 fixed4 color : COLOR;//颜色值  
  73.             };  
  74.   
  75.             //--------------------------------【顶点着色函数】-----------------------------  
  76.             // 输入:顶点输入结构体  
  77.             // 输出:顶点输出结构体  
  78.             //---------------------------------------------------------------------------------  
  79.             vertexOutput vert(vertexInput Input)  
  80.             {  
  81.                 //【1】声明一个输出结构对象  
  82.                 vertexOutput Output;  
  83.   
  84.                 //【2】填充此输出结构  
  85.                 //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  86.                 Output.vertex = mul(UNITY_MATRIX_MVP, Input.vertex);  
  87.                 //输出的纹理坐标也就是输入的纹理坐标  
  88.                 Output.texcoord = Input.texcoord;  
  89.                 //输出的颜色值也就是输入的颜色值  
  90.                 Output.color = Input.color;  
  91.   
  92.                 //【3】返回此输出结构对象  
  93.                 return Output;  
  94.             }  
  95.   
  96.             //--------------------------------【片段着色函数】-----------------------------  
  97.             // 输入:顶点输出结构体  
  98.             // 输出:float4型的颜色值  
  99.             //---------------------------------------------------------------------------------  
  100.             fixed4 frag(vertexOutput Input) : COLOR  
  101.             {  
  102.                 //【1】获取顶点的坐标值  
  103.                 float2 uv = Input.texcoord.xy;  
  104.   
  105.                 //【2】解决平台差异的问题。校正方向,若和规定方向相反,则将速度反向并加1  
  106.                 #if UNITY_UV_STARTS_AT_TOP  
  107.                 if (_MainTex_TexelSize.y < 0)  
  108.                     _DropSpeed = 1 - _DropSpeed;  
  109.                 #endif  
  110.   
  111.                 //【3】设置三层水流效果,按照一定的规律在水滴纹理上分别进行取样  
  112.                 float3 rainTex1 = tex2D(_ScreenWaterDropTex, float2(uv.x * 1.15* _SizeX, (uv.y* _SizeY *1.1) + _CurTime* _DropSpeed *0.15)).rgb / _Distortion;  
  113.                 float3 rainTex2 = tex2D(_ScreenWaterDropTex, float2(uv.x * 1.25* _SizeX - 0.1, (uv.y *_SizeY * 1.2) + _CurTime *_DropSpeed * 0.2)).rgb / _Distortion;  
  114.                 float3 rainTex3 = tex2D(_ScreenWaterDropTex, float2(uv.x* _SizeX *0.9, (uv.y *_SizeY * 1.25) + _CurTime * _DropSpeed* 0.032)).rgb / _Distortion;  
  115.   
  116.                 //【4】整合三层水流效果的颜色信息,存于finalRainTex中  
  117.                 float2 finalRainTex = uv.xy - (rainTex1.xy - rainTex2.xy - rainTex3.xy) / 3;  
  118.   
  119.                 //【5】按照finalRainTex的坐标信息,在主纹理上进行采样  
  120.                 float3 finalColor = tex2D(_MainTex, float2(finalRainTex.x, finalRainTex.y)).rgb;  
  121.   
  122.                 //【6】返回加上alpha分量的最终颜色值  
  123.                 return fixed4(finalColor, 1.0);  
  124.   
  125.   
  126.             }  
  127.   
  128.             //===========结束CG着色器语言编写模块===========  
  129.             ENDCG  
  130.         }  
  131.     }  
  132. }  


 

屏幕特效Shader中真正有营养的核心代码,一般都是位于像素着色器中。也就是这里的frag函数中,稍微聊一聊。

 

第一步,先从顶点着色器输出结构体中获取顶点的坐标:

[cpp]  view plain  copy
 print ?
  1. //【1】获取顶点的坐标值  
  2. float2 uv = Input.texcoord.xy;  


第二步,因为需要水幕纹理在屏幕中从上向下滚动,而不同平台的原点位置是不同的,Direct3D原点在左上角,OpenGL原点在左下角。所以在这边需要对情况进行统一。

统一的方法里用到了UNITY_UV_STARTS_AT_TOP宏,它是Unity中内置的宏,其值 取为1 或0 ; 在纹理 V 坐标在“纹理顶部”为零的平台上值取 1。如Direct3D;而OpenGL 平台上取 0。

另外,这边还用到了MainTex_TexelSize变量。

float2型的MainTex_TexelSize(其实是_TexelSize后缀,前面的名称会根据定义纹理变量的改变而改变)也是Unity中内置的一个变量,存放了纹理中单个像素的尺寸,也就是说,如果有一张2048 x 2048的纹理,那么x和y的取值都为1.0/2048.0。而且需要注意,当该纹理被Direct3D的抗锯齿操作垂直反转过后,xxx_TexelSize的值将为负数。

 

所以,第二步的代码就是如下:

[cpp]  view plain  copy
 print ?
  1. //【2】解决平台差异的问题。校正方向,若和规定方向相反,则将速度反向并加1  
  2.     #if UNITY_UV_STARTS_AT_TOP  
  3.      if(_MainTex_TexelSize.y < 0)  
  4.        _DropSpeed= 1 - _DropSpeed;  
  5.            #endif  

 

第三步,定义三层水流的纹理,按照不同的参数取值,进行采样:

[cpp]  view plain  copy
 print ?
  1.  //【3】设置三层水流效果,按照一定的规律在水滴纹理上分别进行取样  
  2. float3 rainTex1 = tex2D(_ScreenWaterDropTex, float2(uv.x * 1.15* _SizeX, (uv.y* _SizeY*1.1) + _CurTime* _DropSpeed *0.15)).rgb / _Distortion;  
  3. float3 rainTex2 = tex2D(_ScreenWaterDropTex, float2(uv.x * 1.25* _SizeX - 0.1, (uv.y*_SizeY * 1.2) + _CurTime *_DropSpeed * 0.2)).rgb / _Distortion;  
  4. float3 rainTex3 = tex2D(_ScreenWaterDropTex, float2(uv.x* _SizeX *0.9, (uv.y *_SizeY *1.25) + _CurTime * _DropSpeed* 0.032)).rgb / _Distortion;  


第四步,整合一下三层水流效果的颜色信息,用一个float2型的变量存放下来:

[cpp]  view plain  copy
 print ?
  1. //【4】整合三层水流效果的颜色信息,存于finalRainTex中  
  2. float2 finalRainTex = uv.xy - (rainTex1.xy - rainTex2.xy - rainTex3.xy) / 3;  


 

第五步,自然是在屏幕所在的纹理_MainTex中进行一次最终的采样,算出最终结果颜色值,存放于float3型的finalColor中。

[cpp]  view plain  copy
 print ?
  1. //【5】按照finalRainTex的坐标信息,在主纹理上进行采样  
  2. float3 finalColor = tex2D(_MainTex, float2(finalRainTex.x, finalRainTex.y)).rgb;  


第六步,因为返回的是一个fixed4型的变量,rgba。所以需要给float3型的finalColor配上一个alpha分量,并返回:

//【6】返回加上alpha分量的最终颜色值

[cpp]  view plain  copy
 print ?
  1. return fixed4(finalColor, 1.0);  

 

OK,关于此Shader的实现细节,差不多就需要讲到这些。下面再看一下C#脚本文件的实现。

 


 

4.2 C#脚本实现部分

 


C#脚本文件的实现并没有什么好讲的,唯一的地方,水滴纹理的载入,上面已经提到过了,实现代码如下:

[csharp]  view plain  copy
 print ?
  1. //载入素材图  
  2.  ScreenWaterDropTex = Resources.Load("ScreenWaterDrop") asTexture2D;   

 

下面就直接贴出详细注释的实现此特效的C#脚本:

 

[csharp]  view plain  copy
 print ?
  1. //-----------------------------------------------【C#脚本说明】---------------------------------------------------  
  2. //  屏幕水幕特效的实现代码-C#脚本部分  
  3. //      2015年10月  Created by  浅墨  
  4. //      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo  
  5. //---------------------------------------------------------------------------------------------------------------------  
  6.   
  7.   
  8. using UnityEngine;  
  9. using System.Collections;  
  10.   
  11. [ExecuteInEditMode]  
  12. [AddComponentMenu("浅墨Shader编程/Volume9/ScreenWaterDropEffect")]  
  13. public class ScreenWaterDropEffect : MonoBehaviour   
  14. {  
  15.     //-------------------变量声明部分-------------------  
  16.     #region Variables  
  17.   
  18.     //着色器和材质实例  
  19.     public Shader CurShader;//着色器实例  
  20.     private Material CurMaterial;//当前的材质  
  21.   
  22.     //时间变量和素材图的定义  
  23.     private float TimeX = 1.0f;//时间变量  
  24.     private Texture2D ScreenWaterDropTex;//屏幕水滴的素材图  
  25.   
  26.     //可以在编辑器中调整的参数值  
  27.     [Range(5, 64), Tooltip("溶解度")]  
  28.     public float Distortion = 8.0f;  
  29.     [Range(0, 7), Tooltip("水滴在X坐标上的尺寸")]  
  30.     public float SizeX = 1f;  
  31.     [Range(0, 7), Tooltip("水滴在Y坐标上的尺寸")]  
  32.     public float SizeY = 0.5f;  
  33.     [Range(0, 10), Tooltip("水滴的流动速度")]  
  34.     public float DropSpeed = 3.6f;  
  35.   
  36.     //用于参数调节的中间变量  
  37.     public static float ChangeDistortion;  
  38.     public static float ChangeSizeX;  
  39.     public static float ChangeSizeY;  
  40.     public static float ChangeDropSpeed;  
  41.     #endregion  
  42.   
  43.   
  44.     //-------------------------材质的get&set----------------------------  
  45.     #region MaterialGetAndSet  
  46.     Material material  
  47.     {  
  48.         get  
  49.         {  
  50.             if (CurMaterial == null)  
  51.             {  
  52.                 CurMaterial = new Material(CurShader);  
  53.                 CurMaterial.hideFlags = HideFlags.HideAndDontSave;  
  54.             }  
  55.             return CurMaterial;  
  56.         }  
  57.     }  
  58.     #endregion  
  59.   
  60.   
  61.     //-----------------------------------------【Start()函数】---------------------------------------------    
  62.     // 说明:此函数仅在Update函数第一次被调用前被调用  
  63.     //--------------------------------------------------------------------------------------------------------  
  64.     void Start()  
  65.     {  
  66.         //依次赋值  
  67.         ChangeDistortion = Distortion;  
  68.         ChangeSizeX = SizeX;  
  69.         ChangeSizeY = SizeY;  
  70.         ChangeDropSpeed = DropSpeed;  
  71.   
  72.         //载入素材图  
  73.         ScreenWaterDropTex = Resources.Load("ScreenWaterDrop"as Texture2D;  
  74.   
  75.         //找到当前的Shader文件  
  76.         CurShader = Shader.Find("浅墨Shader编程/Volume9/ScreenWaterDropEffect");  
  77.   
  78.         //判断是否支持屏幕特效  
  79.         if (!SystemInfo.supportsImageEffects)  
  80.         {  
  81.             enabled = false;  
  82.             return;  
  83.         }  
  84.     }  
  85.   
  86.     //-------------------------------------【OnRenderImage()函数】------------------------------------    
  87.     // 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果  
  88.     //--------------------------------------------------------------------------------------------------------  
  89.     void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)  
  90.     {  
  91.         //着色器实例不为空,就进行参数设置  
  92.         if (CurShader != null)  
  93.         {  
  94.             //时间的变化  
  95.             TimeX += Time.deltaTime;  
  96.             //时间大于100,便置0,保证可以循环  
  97.             if (TimeX > 100) TimeX = 0;  
  98.   
  99.             //设置Shader中其他的外部变量  
  100.             material.SetFloat("_CurTime", TimeX);  
  101.             material.SetFloat("_Distortion", Distortion);  
  102.             material.SetFloat("_SizeX", SizeX);  
  103.             material.SetFloat("_SizeY", SizeY);  
  104.             material.SetFloat("_DropSpeed", DropSpeed);  
  105.             material.SetTexture("_ScreenWaterDropTex", ScreenWaterDropTex);  
  106.   
  107.             //拷贝源纹理到目标渲染纹理,加上我们的材质效果  
  108.             Graphics.Blit(sourceTexture, destTexture, material);  
  109.         }  
  110.         //着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的  
  111.         else  
  112.         {  
  113.             //直接拷贝源纹理到目标渲染纹理  
  114.             Graphics.Blit(sourceTexture, destTexture);  
  115.         }  
  116.   
  117.   
  118.     }  
  119.   
  120.     //-----------------------------------------【OnValidate()函数】--------------------------------------    
  121.     // 说明:此函数在编辑器中该脚本的某个值发生了改变后被调用  
  122.     //--------------------------------------------------------------------------------------------------------  
  123.     void OnValidate()  
  124.     {  
  125.         ChangeDistortion = Distortion;  
  126.         ChangeSizeX = SizeX;  
  127.         ChangeSizeY = SizeY;  
  128.         ChangeDropSpeed = DropSpeed;  
  129.     }  
  130.   
  131.   
  132.     //-----------------------------------------【Update()函数】------------------------------------------    
  133.     // 说明:此函数在每一帧中都会被调用  
  134.     //--------------------------------------------------------------------------------------------------------   
  135.     void Update()  
  136.     {  
  137.         //若程序在运行,进行赋值  
  138.         if (Application.isPlaying)  
  139.         {  
  140.             //赋值  
  141.             Distortion = ChangeDistortion;  
  142.             SizeX = ChangeSizeX;  
  143.             SizeY = ChangeSizeY;  
  144.             DropSpeed = ChangeDropSpeed;  
  145.         }  
  146.         //找到对应的Shader文件,和纹理素材  
  147. #if UNITY_EDITOR  
  148.         if (Application.isPlaying != true)  
  149.         {  
  150.             CurShader = Shader.Find("浅墨Shader编程/Volume9/ScreenWaterDropEffect");  
  151.             ScreenWaterDropTex = Resources.Load("ScreenWaterDrop"as Texture2D;  
  152.   
  153.         }  
  154. #endif  
  155.   
  156.     }  
  157.   
  158.     //-----------------------------------------【OnDisable()函数】---------------------------------------    
  159.     // 说明:当对象变为不可用或非激活状态时此函数便被调用    
  160.     //--------------------------------------------------------------------------------------------------------  
  161.     void OnDisable()  
  162.     {  
  163.         if (CurMaterial)  
  164.         {  
  165.             //立即销毁材质实例  
  166.             DestroyImmediate(CurMaterial);  
  167.         }  
  168.   
  169.     }  
  170. }  


OK,水幕屏幕特效实现部分大致就是这样,下面看一下运行效果的对比。

 

 


 

五、最终的效果展示

 

 


贴几张场景的效果图和使用了屏幕特效后的效果图。在试玩场景时,除了类似CS/CF的FPS游戏控制系统以外,还可以使用键盘上的按键【F】,开启或者屏幕特效。

本次的场景还是使用上次更新中放出的城镇,只是出生地点有所改变。为了有更多的时间专注于Shader书写本身,以后的博文配套场景采取不定期大更新的形式(两次、三次一换)。


 

城镇野外(with 屏幕水幕特效):



城镇野外(原始图):



山坡上(with 屏幕水幕特效):



山坡上(原始图):



城镇中(with 屏幕水幕特效):



城镇中(原始图)



石桥上(with 屏幕水幕特效):



石桥上(原始图):

 

 

 

本次的更新大致如此,感谢各位捧场,我们下周再见。

 

 

附: 本博文相关下载链接清单

 

【百度云】博文示例场景exe下载

【百度云】包含博文示例场景所有资源与源码的unitypackage下载

【Github】Unity5 Standard Shader主文件源码逐行注释

【Github】水幕屏幕特效实现源码


另附:若遇到导入unitypackage过程中进度条卡住的情况,不用慌,这是Unity5的一个bug。我也经常在导入工程时遇到。其实这个时候已经导入成功了,用资源管理器杀掉当前这个Unity的进程,再打开就行了。

 

本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

文章链接: http://blog.csdn.net/poem_qianmo/article/details/49556461

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

本文工程使用的Unity3D版本: 5.2.1 


 

 

概要:本文主要介绍了Unity5中的标准着色器,并且也涉及到了基于物理的着色、延迟渲染等高级着色技术,而在文章后半部分,也对屏幕水幕特效的实现方法进行了讲解与分析。


 

依然是附上一组本文配套工程的运行截图之后,便开始我们的正文。如下图。

 

打开水幕特效的效果图:

 

 

原始的城镇场景:

 

 

需要说明,这里的水幕特效是动态的水流效果。本来准备传GIF上来展示动态的效果,但受图片尺寸2M的限制,无法出色地表现出动态效果(帧数有限,图片清晰度也会大打折扣)。建议感兴趣的朋友们下载这里提供的游戏场景的exe,自己在机器上体验水幕效果,比看博文中贴出的图片效果好太多。

 

【可运行的本文配套exe游戏场景请点击这里下载】

 

提示:在此游戏场景中按F键可以开关屏幕特效。

 

在文章末尾有更多的运行截图,并提供了源工程的下载。

 

好的,正文开始。

 

 

 



一、认识Unity5中的Standard Shader

 



Unity5中重点推出了一套基于物理的着色(Physically Based Shading,PBS)的多功能Shader,叫做标准着色器(Standard Shader)。这套Shader的设计初衷是化繁为简。想用这样的一个多功能Shader,来代替之前多种多样的Shader各司其职,对于不同的材质效果,需要不同的Shader的局面。

 Unity 5中目前有两个标准着色器,一个名为Standard,我们称它为标准着色器的标准版,另一个名为Standard(Specular Setup),我们称它为标准着色器的高光版,它们共同组成了一个完整的PBS光照明模型,且非常易于使用。其实这两个Shader基本差不多,只是有细微的属性参数上的区别。标准版这边的_Metallic(金属性)、_MetallicGlossMap(金属光泽贴图),被高光版的_SpecColor(高光颜色)、_SpecGlossMap(高光颜色法线贴图)所代替。

标准着色器主要是针对硬质表面(也就是建筑材质)而设计的,可以处理大多数现实世界的材质,例如石头、陶瓷、铜器、银器或橡胶等。同时,它也可以非常出色地处理一些非硬质表面的材质,例如皮肤、头发或布料等。

 

如下的一个Unity官方放出的名为VikingVillage的场景中,所有的物体材质都是使用的标准着色器,标准着色器的广泛适用性可见一斑。

 

 

而在Unity5的官方放出的演示demo视频中,也是用一个标准着色器,Standard(Specular Setup),就完成了游戏场景中绝大多数物体的材质显示。

Unity5 Standard Shader的演示Demo可以看这里:

http://www.iqiyi.com/w_19rquxac31.html


 Unity5 PBS技术的演示Demo截图:




下面稍微对Standard shader中核心的概念——基于物理的着色做一个大概的了解。



 


1.1 基于物理的着色(Physically Based Shading)技术概览

 

 


基于物理的着色(Physically Based Shading,简称PBS)就是以某种方式模拟现实中材质和光照的相互作用的一种着色方法。这种方法在需要光照和材质更加直观和逼真地协同工作的场合下优势非常明显。基于物理的着色模拟了光线在现实中的行为,实现了在不同的光照条件下的逼真效果。为实现这种效果需要遵循各种物理原理,包括能量守恒(也就是物体反射出去的光量不可能超过所接收的光量),Fresnel反射(所有表面反射在掠射角(grazing angles)处更加强烈),以及物体表面是如何自我遮挡等原理。

其实,PBS技术在多年前于虚幻三游戏引擎中就已经被广泛使用,而Unity直到最近刚发布的第五代,才将此特性作为官方的特性实现出来。而在Unity5发布之前,Unity的Asset Store中,已经有不少的第三方插件,在Unity中实现了PBS技术。

 

关于PBS技术,知乎上有一个专栏,专门介绍PBS技术的一些相关原理,在这里推荐一下:

 

基于物理着色(一)

基于物理着色(二)- Microfacet材质和多层材质

基于物理着色(三)- Disney和UE4的实现


这边贴一张专栏中的配套渲染图,效果非常出色:


 

 


 

1.2 如何使用标准着色器

 


可以在Unity5中任意材质的Shader选项中的最前面看到两个Standard Shader字样的选项。其中第一个是普通版,第二个带Specularsetup是高光版。

 


标准着色器标准版材质界面:


 

标准着色器的高光版材质界面:



 

不难得知,标准着色器引入了新的材质编辑器,它使PBS的材质编辑工作比以前的非PBS材质更简单。新的编辑器不但简洁,还提供了材质的所有可能用到的选项。在新编辑器中,我们不需要选用不同的着色器来改变纹理通道;不会再出现 “texture unused, please choose another shader” 这样的提示;也不再需要通过切换着色器来改变混合模式。

所有的纹理通道都是备选的,无需强制使用,任何一个闲置通道的相关代码都会在编译时被优化掉,因此完全不用担心效率方面的问题。unity会根据我们输入到编辑器中的数据来生成正确的代码,并使整个过程保持高效。

 

另外,我们可以用ctrl+点击纹理的方式预览大图,并且还可以分别查看颜色和Alpha通道。

ctrl+点击纹理,得到的纹理放大的预览:


 

OK,关于Unity5中的StandardShader的概念,大概先就讲这么多。更多细节可以参考Unity官方文档:

http://docs.unity3d.com/Manual/shader-StandardShader.html

 

以及这里Unity5 Standard Shader的官方文档论坛翻译版:

http://forum.china.unity3d.com/thread-897-1-1.html

 

 

 

 

1.3 理解标准着色器的组成


上面说了很多标准着色器的强大之处,其实,作为一个着色器,它也没有什么神秘的地方,无非就是两个Shader源文件,加上一堆CG头文件组成的两个功能稍微复杂全面一些的Shader而已。其中,两个Shader源文件里,又按渲染路径分为了很多的SubShader,每个SubShader里面又分为了很多Pass。而CG文件中,主要包含了Shader的支持函数,相关的宏等为Shader源文件提供支持的代码。

 

Unity5中标准着色器的组成,归纳概括如下:

 

  • 两个Shader源文件
  • 七个CG头文件
  • 一个脚本文件(用于自定义材质编辑器UI)

 

下面分别对每个文件进行一个简单的介绍。

 

1)两个Shader源文件


  • Stardard.shader着色器源文件 - 标准着色器的标准版
  • StardardSpecular.shader着色器源文件 - 标准着色器的高光版

 

2)七个CG头文件


  • UnityStandardBRDF.cginc头文件-用于存放标准着色器处理BRDF材质属性相关的函数与宏
  • UnityStandardConfig.cginc头文件-用于存放标准着色器配置相关的代码(其实里面就几个宏)
  • UnityStandardCore.cginc头文件-用于存放标准着色器的主要代码(如顶点着色函数、片段着色函数等相关函数)
  • UnityStandardInput.cginc头文件-用于存放标准着色器输入结构相关的工具函数与宏
  • UnityStandardMeta.cginc头文件-用于存放标准着色器meta通道中会用到的工具函数与宏
  • UnityStandardShadow.cginc头文件-用于存放标准着色器阴影贴图采样相关的工具函数与宏
  • UnityStandardUtils.cginc头文件-用于存放标准着色器共用的一些工具函数

  

在包括本文在内的接下来的几次更新中,本系列文章将试着花篇幅来剖析这两个Shader源文件,和依附的几个CG头文件的源码,从而一窥Unity5中新版Shader书写体系的究竟。

 

 

3)一个脚本文件


  • StandardShaderGUI.cs脚本文件——定义了特定的自定义编辑器UI界面



标准着色器对应材质的编辑器外观不同于一般的Shader,就是因为在Shader末尾书写了如下的代码: 

 

[csharp]  view plain  copy
 print ?
  1. //使用特定的自定义编辑器UI界面  
  2. CustomEditor "StandardShaderGUI"  


 

标准着色器对应材质的编辑器外观如下:

 

 

 

一般的着色器对应材质的编辑器外观如下:


 

 

 

 



二、Unity5标准着色器源代码剖析之一:架构分析篇




上文已经提到过,标准着色器源代码的剖析是一个小小的马拉松,完全解析起来篇幅会很长,所以本系列文章将对剖析的过程进行连载,此节为连载的第一部分。

 

在这里先贴出经过浅墨详细注释的标准着色器标准版的源代码,并对架构进行简单的分析,而对每个通道的剖析在稍后的更新中会进行。

 

[cpp]  view plain  copy
 print ?
  1. //-----------------------------------------------【Shader说明】---------------------------------------------------  
  2. //       Unity5.2.1 Built-in Standard Shader  
  3. //      2015年10月  Commented by  浅墨      
  4. //      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo  
  5. //---------------------------------------------------------------------------------------------------------------------  
  6.   
  7. Shader "Standard"  
  8. {  
  9.     //------------------------------------【属性值】------------------------------------  
  10.     Properties  
  11.     {  
  12.         //主颜色  
  13.         _Color("Color", Color) = (1,1,1,1)  
  14.         //主纹理  
  15.         _MainTex("Albedo", 2D) = "white" {}  
  16.   
  17.         //Alpha剔除值  
  18.         _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5  
  19.         //平滑、光泽度  
  20.         _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5  
  21.   
  22.         //金属性  
  23.         [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0  
  24.         //金属光泽纹理图  
  25.         _MetallicGlossMap("Metallic", 2D) = "white" {}  
  26.   
  27.         //凹凸的尺度  
  28.         _BumpScale("Scale", Float) = 1.0  
  29.         //法线贴图  
  30.         _BumpMap("Normal Map", 2D) = "bump" {}  
  31.   
  32.         //高度缩放尺度  
  33.         _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02  
  34.         //高度纹理图  
  35.         _ParallaxMap ("Height Map", 2D) = "black" {}  
  36.   
  37.         //遮挡强度  
  38.         _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0  
  39.         //遮挡纹理图  
  40.         _OcclusionMap("Occlusion", 2D) = "white" {}  
  41.   
  42.         //自发光颜色  
  43.         _EmissionColor("Color", Color) = (0,0,0)  
  44.         //自发光纹理图  
  45.         _EmissionMap("Emission", 2D) = "white" {}  
  46.           
  47.         //细节掩膜图  
  48.         _DetailMask("Detail Mask", 2D) = "white" {}  
  49.   
  50.         //细节纹理图  
  51.         _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}  
  52.         //细节法线贴图尺度  
  53.         _DetailNormalMapScale("Scale", Float) = 1.0  
  54.         //细节法线贴图  
  55.         _DetailNormalMap("Normal Map", 2D) = "bump" {}  
  56.   
  57.         //二级纹理的UV设置  
  58.         [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0  
  59.   
  60.         //混合状态的定义  
  61.         [HideInInspector] _Mode ("__mode", Float) = 0.0  
  62.         [HideInInspector] _SrcBlend ("__src", Float) = 1.0  
  63.         [HideInInspector] _DstBlend ("__dst", Float) = 0.0  
  64.         [HideInInspector] _ZWrite ("__zw", Float) = 1.0  
  65.     }  
  66.   
  67.     //===========开始CG着色器语言编写模块===========  
  68.     CGINCLUDE  
  69.         //BRDF相关的一个宏  
  70.         #define UNITY_SETUP_BRDF_INPUT MetallicSetup  
  71.     //===========结束CG着色器语言编写模块===========  
  72.     ENDCG  
  73.   
  74.   
  75.     //------------------------------------【子着色器1】------------------------------------  
  76.     // 此子着色器用于Shader Model 3.0  
  77.     //----------------------------------------------------------------------------------------  
  78.     SubShader  
  79.     {  
  80.         //渲染类型设置:不透明  
  81.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }  
  82.   
  83.         //细节层次设为:300  
  84.         LOD 300  
  85.           
  86.         //--------------------------------通道1-------------------------------  
  87.         // 正向基础渲染通道(Base forward pass)  
  88.         // 处理方向光,自发光,光照贴图等 ...  
  89.         Pass  
  90.         {  
  91.             //设置通道名称  
  92.             Name "FORWARD"   
  93.   
  94.             //于通道标签中设置光照模型为ForwardBase,正向渲染基础通道  
  95.             Tags { "LightMode" = "ForwardBase" }  
  96.   
  97.             //混合操作:源混合乘以目标混合  
  98.             Blend [_SrcBlend] [_DstBlend]  
  99.             // 根据_ZWrite参数,设置深度写入模式开关与否  
  100.             ZWrite [_ZWrite]  
  101.   
  102.             //===========开启CG着色器语言编写模块===========  
  103.             CGPROGRAM  
  104.   
  105.             //着色器编译目标:Model 3.0  
  106.             #pragma target 3.0  
  107.   
  108.             //编译指令:不使用GLES渲染器编译  
  109.             #pragma exclude_renderers gles  
  110.               
  111.             // ---------编译指令:着色器编译多样化--------  
  112.             #pragma shader_feature _NORMALMAP  
  113.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  114.             #pragma shader_feature _EMISSION  
  115.             #pragma shader_feature _METALLICGLOSSMAP   
  116.             #pragma shader_feature ___ _DETAIL_MULX2  
  117.             #pragma shader_feature _PARALLAXMAP  
  118.               
  119.             //--------着色器编译多样化快捷指令------------  
  120.             //编译指令:编译正向渲染基础通道(用于正向渲染中,应用环境光照、主方向光照和顶点/球面调和光照)所需的所有变体。  
  121.             //这些变体用于处理不同的光照贴图类型、主要方向光源的阴影选项的开关与否  
  122.             #pragma multi_compile_fwdbase  
  123.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  124.             #pragma multi_compile_fog  
  125.               
  126.             //编译指令:告知编译器顶点和片段着色函数的名称  
  127.             #pragma vertex vertForwardBase  
  128.             #pragma fragment fragForwardBase  
  129.   
  130.             //包含辅助CG头文件  
  131.             #include "UnityStandardCore.cginc"  
  132.   
  133.             //===========结束CG着色器语言编写模块===========  
  134.             ENDCG  
  135.         }  
  136.         //--------------------------------通道2-------------------------------  
  137.         // 正向附加渲染通道(Additive forward pass)  
  138.         // 以每个光照一个通道的方式应用附加的逐像素光照  
  139.         Pass  
  140.         {  
  141.             //设置通道名称  
  142.             Name "FORWARD_DELTA"  
  143.   
  144.             //于通道标签中设置光照模型为ForwardAdd,正向渲染附加通道  
  145.             Tags { "LightMode" = "ForwardAdd" }  
  146.   
  147.             //混合操作:源混合乘以1  
  148.             Blend [_SrcBlend] One  
  149.   
  150.             //附加通道中的雾效应该为黑色  
  151.             Fog { Color (0,0,0,0) }   
  152.   
  153.             //关闭深度写入模式  
  154.             ZWrite Off  
  155.             //设置深度测试模式:小于等于  
  156.             ZTest LEqual  
  157.   
  158.             //===========开启CG着色器语言编写模块===========  
  159.             CGPROGRAM  
  160.   
  161.             //着色器编译目标:Model 3.0  
  162.             #pragma target 3.0  
  163.             //编译指令:不使用GLES渲染器编译  
  164.             #pragma exclude_renderers gles  
  165.   
  166.             // ---------编译指令:着色器编译多样化--------  
  167.             #pragma shader_feature _NORMALMAP  
  168.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  169.             #pragma shader_feature _METALLICGLOSSMAP  
  170.             #pragma shader_feature ___ _DETAIL_MULX2  
  171.             #pragma shader_feature _PARALLAXMAP  
  172.               
  173.             //--------使用Unity内置的着色器编译多样化快捷指令------------  
  174.             //编译指令:编译正向渲染基础通道所需的所有变体,但同时为上述通道的处理赋予了光照实时阴影的能力。  
  175.             #pragma multi_compile_fwdadd_fullshadows  
  176.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  177.             #pragma multi_compile_fog  
  178.   
  179.             //编译指令:告知编译器顶点和片段着色函数的名称  
  180.             #pragma vertex vertForwardAdd  
  181.             #pragma fragment fragForwardAdd  
  182.   
  183.             //包含辅助CG头文件  
  184.             #include "UnityStandardCore.cginc"  
  185.   
  186.             //===========结束CG着色器语言编写模块===========  
  187.             ENDCG  
  188.         }  
  189.   
  190.         // --------------------------------通道3-------------------------------  
  191.         //  阴影渲染通道(Shadow Caster pass)  
  192.         //  将将物体的深度渲染到阴影贴图或深度纹理中  
  193.         Pass   
  194.         {  
  195.             //设置通道名称  
  196.             Name "ShadowCaster"  
  197.             //于通道标签中设置光照模型为ShadowCaster。  
  198.             //此光照模型代表着将物体的深度渲染到阴影贴图或深度纹理。  
  199.             Tags { "LightMode" = "ShadowCaster" }  
  200.   
  201.             //开启深入写入模式  
  202.             ZWrite On   
  203.             //设置深度测试模式:小于等于  
  204.             ZTest LEqual  
  205.   
  206.             //===========开启CG着色器语言编写模块===========  
  207.             CGPROGRAM  
  208.   
  209.             //着色器编译目标:Model 3.0  
  210.             #pragma target 3.0  
  211.   
  212.             //编译指令:不使用GLES渲染器编译  
  213.             #pragma exclude_renderers gles  
  214.               
  215.   
  216.             // ---------编译指令:着色器编译多样化--------  
  217.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  218.               
  219.             //--------着色器编译多样化快捷指令------------  
  220.             //进行阴影投射相关的多着色器变体的编译  
  221.             #pragma multi_compile_shadowcaster  
  222.   
  223.             //编译指令:告知编译器顶点和片段着色函数的名称  
  224.             #pragma vertex vertShadowCaster  
  225.             #pragma fragment fragShadowCaster  
  226.               
  227.             //包含辅助CG头文件  
  228.             #include "UnityStandardShadow.cginc"  
  229.   
  230.             //===========结束CG着色器语言编写模块===========  
  231.             ENDCG  
  232.         }  
  233.   
  234.         // --------------------------------通道4-------------------------------  
  235.         //  延迟渲染通道(Deferred Render Pass)  
  236.         Pass  
  237.         {  
  238.             //设置通道名称  
  239.             Name "DEFERRED"  
  240.             //于通道标签中设置光照模型为Deferred,延迟渲染通道  
  241.             Tags { "LightMode" = "Deferred" }  
  242.   
  243.             CGPROGRAM  
  244.             #pragma target 3.0  
  245.             // TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT  
  246.             #pragma exclude_renderers nomrt gles  
  247.               
  248.   
  249.             //---------编译指令:着色器编译多样化(shader_feature)--------  
  250.             #pragma shader_feature _NORMALMAP  
  251.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  252.             #pragma shader_feature _EMISSION  
  253.             #pragma shader_feature _METALLICGLOSSMAP  
  254.             #pragma shader_feature ___ _DETAIL_MULX2  
  255.             #pragma shader_feature _PARALLAXMAP  
  256.   
  257.             //---------编译指令:着色器编译多样化(multi_compile)--------  
  258.             #pragma multi_compile ___ UNITY_HDR_ON  
  259.             #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON  
  260.             #pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE  
  261.             #pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON  
  262.               
  263.             //编译指令:告知编译器顶点和片段着色函数的名称  
  264.             #pragma vertex vertDeferred  
  265.             #pragma fragment fragDeferred  
  266.   
  267.             //包含辅助CG头文件  
  268.             #include "UnityStandardCore.cginc"  
  269.   
  270.             //===========结束CG着色器语言编写模块===========  
  271.             ENDCG  
  272.         }  
  273.   
  274.         // --------------------------------通道5-------------------------------  
  275.         //元通道(Meta Pass)  
  276.         //为全局光照(GI),光照贴图等技术提取相关参数,如(emission, albedo等参数值)  
  277.         //此通道并不在常规的渲染过程中使用  
  278.         Pass  
  279.         {  
  280.             //设置通道名称  
  281.             Name "META"   
  282.   
  283.             //于通道标签中设置光照模型为Meta  
  284.             //(截止2015年10月22日,Unity 5.2.1的官方文档中并没有收录此光照模型,应该是Unity官方的疏漏)  
  285.             Tags { "LightMode"="Meta" }  
  286.             //关闭剔除操作  
  287.             Cull Off  
  288.   
  289.             //===========开启CG着色器语言编写模块===========  
  290.             CGPROGRAM  
  291.   
  292.             //编译指令:告知编译器顶点和片段着色函数的名称  
  293.             #pragma vertex vert_meta  
  294.             #pragma fragment frag_meta  
  295.   
  296.             //---------编译指令:着色器编译多样化--------  
  297.             #pragma shader_feature _EMISSION  
  298.             #pragma shader_feature _METALLICGLOSSMAP  
  299.             #pragma shader_feature ___ _DETAIL_MULX2  
  300.   
  301.             //包含辅助CG头文件  
  302.             #include "UnityStandardMeta.cginc"  
  303.   
  304.             //===========结束CG着色器语言编写模块===========  
  305.             ENDCG  
  306.         }  
  307.     }  
  308.   
  309.     //------------------------------------【子着色器2】-----------------------------------  
  310.     // 此子着色器用于Shader Model 2.0  
  311.     //----------------------------------------------------------------------------------------  
  312.     SubShader  
  313.     {  
  314.         //渲染类型设置:不透明  
  315.         Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }  
  316.         //细节层次设为:150  
  317.         LOD 150  
  318.   
  319.         //--------------------------------通道1-------------------------------  
  320.         // 正向基础渲染通道(Base forward pass)  
  321.         // 处理方向光,自发光,光照贴图等 ...  
  322.         Pass  
  323.         {  
  324.             //设置通道名称  
  325.             Name "FORWARD"   
  326.             //于通道标签中设置光照模型为ForwardBase,正向渲染基础通道  
  327.             Tags { "LightMode" = "ForwardBase" }  
  328.             //混合操作:源混合乘以目标混合,即结果为两者的混合  
  329.             Blend [_SrcBlend] [_DstBlend]  
  330.             // 根据_ZWrite参数,设置深度写入模式开关与否  
  331.             ZWrite [_ZWrite]  
  332.   
  333.             //===========开启CG着色器语言编写模块===========  
  334.             CGPROGRAM  
  335.             //着色器编译目标:Model 2.0  
  336.             #pragma target 2.0  
  337.   
  338.             // ---------编译指令:着色器编译多样化--------  
  339.             #pragma shader_feature _NORMALMAP  
  340.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  341.             #pragma shader_feature _EMISSION   
  342.             #pragma shader_feature _METALLICGLOSSMAP   
  343.             #pragma shader_feature ___ _DETAIL_MULX2  
  344.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP  
  345.   
  346.             //跳过如下变体的编译,简化编译过程  
  347.             #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE  
  348.               
  349.             //--------着色器编译多样化快捷指令------------  
  350.             #pragma multi_compile_fwdbase  
  351.             #pragma multi_compile_fog  
  352.   
  353.             //编译指令:告知编译器顶点和片段着色函数的名称  
  354.             #pragma vertex vertForwardBase  
  355.             #pragma fragment fragForwardBase  
  356.   
  357.             //包含辅助CG头文件  
  358.             #include "UnityStandardCore.cginc"  
  359.   
  360.             //===========结束CG着色器语言编写模块===========  
  361.             ENDCG  
  362.         }  
  363.   
  364.         //--------------------------------通道2-------------------------------  
  365.         // 正向附加渲染通道(Additive forward pass)  
  366.         // 以每个光照一个通道的方式应用附加的逐像素光照  
  367.         Pass  
  368.         {  
  369.             //设置通道名称  
  370.             Name "FORWARD_DELTA"  
  371.   
  372.             //于通道标签中设置光照模型为ForwardAdd,正向渲染附加通道  
  373.             Tags { "LightMode" = "ForwardAdd" }  
  374.   
  375.             //混合操作:源混合乘以1  
  376.             Blend [_SrcBlend] One  
  377.   
  378.             //附加通道中的雾效应该为黑色  
  379.             Fog { Color (0,0,0,0) }   
  380.   
  381.             //关闭深度写入模式  
  382.             ZWrite Off  
  383.   
  384.             //设置深度测试模式:小于等于  
  385.             ZTest LEqual  
  386.   
  387.             //===========开启CG着色器语言编写模块===========  
  388.             CGPROGRAM  
  389.             //着色器编译目标:Model 2.0  
  390.             #pragma target 2.0  
  391.   
  392.             // ---------编译指令:着色器编译多样化--------  
  393.             #pragma shader_feature _NORMALMAP  
  394.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  395.             #pragma shader_feature _METALLICGLOSSMAP  
  396.             #pragma shader_feature ___ _DETAIL_MULX2  
  397.   
  398.             //跳过一些变体的编译  
  399.             // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP  
  400.             #pragma skip_variants SHADOWS_SOFT  
  401.   
  402.             //--------使用Unity内置的着色器编译多样化快捷指令------------  
  403.             //编译指令:编译正向渲染基础通道所需的所有变体,但同时为上述通道的处理赋予了光照实时阴影的能力。  
  404.             #pragma multi_compile_fwdadd_fullshadows  
  405.             //编译指令:编译几个不同变种来处理不同类型的雾效(关闭/线性/指数/二阶指数/)  
  406.             #pragma multi_compile_fog  
  407.   
  408.             //编译指令:告知编译器顶点和片段着色函数的名称  
  409.             #pragma vertex vertForwardAdd  
  410.             #pragma fragment fragForwardAdd  
  411.   
  412.             //包含辅助CG头文件  
  413.             #include "UnityStandardCore.cginc"  
  414.   
  415.             //===========结束CG着色器语言编写模块===========  
  416.             ENDCG  
  417.         }  
  418.   
  419.         // --------------------------------通道3-------------------------------  
  420.         //  阴影渲染通道(Shadow Caster pass)  
  421.         //  将将物体的深度渲染到阴影贴图或深度纹理中  
  422.         Pass   
  423.         {  
  424.             //设置通道名称  
  425.             Name "ShadowCaster"  
  426.   
  427.             //于通道标签中设置光照模型为ShadowCaster。  
  428.             //此光照模型代表着将物体的深度渲染到阴影贴图或深度纹理。  
  429.             Tags { "LightMode" = "ShadowCaster" }  
  430.   
  431.             //开启深入写入模式  
  432.             ZWrite On  
  433.   
  434.             //设置深度测试模式:小于等于  
  435.             ZTest LEqual  
  436.   
  437.             //===========开启CG着色器语言编写模块===========  
  438.             CGPROGRAM  
  439.             //着色器编译目标:Model 2.0  
  440.             #pragma target 2.0  
  441.   
  442.             //---------编译指令:着色器编译多样化(shader_feature)--------  
  443.             #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON  
  444.               
  445.             //编译指令:跳过某些变体的编译  
  446.             #pragma skip_variants SHADOWS_SOFT  
  447.   
  448.             //快捷编译指令:进行阴影投射相关的多着色器变体的编译  
  449.             #pragma multi_compile_shadowcaster  
  450.   
  451.             //编译指令:告知编译器顶点和片段着色函数的名称  
  452.             #pragma vertex vertShadowCaster  
  453.             #pragma fragment fragShadowCaster  
  454.   
  455.             //包含辅助CG头文件  
  456.             #include "UnityStandardShadow.cginc"  
  457.   
  458.             //===========结束CG着色器语言编写模块===========  
  459.             ENDCG  
  460.         }  
  461.   
  462.         // --------------------------------通道4-------------------------------  
  463.         //元通道(Meta Pass)  
  464.         //为全局光照(GI),光照贴图等技术提取相关参数,如(emission, albedo等参数值)  
  465.         //此通道并不在常规的渲染过程中使用  
  466.         Pass  
  467.         {  
  468.             //设置通道名称  
  469.             Name "META"   
  470.   
  471.             //于通道标签中设置光照模型为Meta  
  472.             //(截止2015年10月22日,Unity 5.2.1的官方文档中并没有收录此光照模型,应该是Unity官方的疏漏)  
  473.             Tags { "LightMode"="Meta" }  
  474.             //关闭剔除操作  
  475.             Cull Off  
  476.   
  477.             //===========开启CG着色器语言编写模块===========  
  478.             CGPROGRAM  
  479.   
  480.             //编译指令:告知编译器顶点和片段着色函数的名称  
  481.             #pragma vertex vert_meta  
  482.             #pragma fragment frag_meta  
  483.   
  484.             //---------编译指令:着色器编译多样化--------  
  485.             #pragma shader_feature _EMISSION  
  486.             #pragma shader_feature _METALLICGLOSSMAP  
  487.             #pragma shader_feature ___ _DETAIL_MULX2  
  488.   
  489.             //包含辅助CG头文件  
  490.             #include "UnityStandardMeta.cginc"  
  491.   
  492.             //===========结束CG着色器语言编写模块===========  
  493.             ENDCG  
  494.         }  
  495.     }  
  496.   
  497.     //回退Shader为顶点光照Shader  
  498.     FallBack "VertexLit"  
  499.     //使用特定的自定义编辑器UI界面  
  500.     CustomEditor "StandardShaderGUI"  
  501. }  

 

标准着色器的源代码部分相对于着色器的体量来说,算是有点长的,加上注释后有近500行。先稍微把它的架构稍微理一下。

标准着色器由两个SubShader组成。第一个SubShader用于处理Shader Model 3.0,有5个通道,第二个SubShader用于处理Shader Model 2.0, 有4个通道,

详细的架构如下图:

 


OK,标准着色器的架构大致如上。今天就先讲这么多,上面代码的稍微有些看不懂没关系,从下次更新开始,就将深入到每个Pass的顶点和片段着色器函数之中,逐行注释与分析他们的指令细节。

 

而不难发现,标准着色器代码的第一个SubShader比第二个SubShader多出了一个延迟渲染通道(Deferred Render Pass),下面稍微提一下延迟渲染的基本概念。

 

 

 



 

三、关于延迟渲染(Deferred Render)





看过《GPU Gems2》的朋友们应该都有所了解,延迟渲染(Deferred Render,又称Deferred Shading)是次时代引擎必备的渲染方式之一。

需要注意,Deferred Render和Deferred Shading有一点细微的差别。

  • Deferred Shading:将所有的Shading全部转到Deferred阶段进行。
  • Deferred Render:只是有选择地将Lighting转到Deferred阶段进行。

Unity中默认使用的是Deferred Shading。

 

而延迟渲染,一言以蔽之,就是将光照/渲染的计算延迟到第二步进行,避免多次渲染同一个像素,从而减少多余的计算操作,以提高渲染效率的一种先进的渲染方式。

 

延迟渲染最大的优势是可以实现同屏中数量众多的动态光源(十几到几十个),这在传统的渲染管线中是很难实现的。

 

更多延迟渲染的细节,这边不细说,只是提供一些链接,以作进一步了解延迟渲染的导论之用:


  • https://zh.wikipedia.org/wiki/%E5%BB%B6%E6%9C%9F%E7%9D%80%E8%89%B2 
  • http://blog.csdn.net/noslopforever/article/details/3951273 
  • http://blog.sina.com.cn/s/blog_458f871201017i06.html 
  • http://www.cnblogs.com/lancidie/archive/2011/08/18/2144748.html 
  • http://blog.csdn.net/pizi0475/article/details/7932920
  • http://www.cnblogs.com/wangchengfeng/p/3440097.html
  • http://blog.csdn.net/bugrunner/article/details/7436600

 

 

 

 


四、屏幕水幕特效的实现

 

 


在上一篇文章中有提到过,Unity中的屏幕特效通常分为两部分来实现:

 

  • Shader实现部分
  • 脚本实现部分

 

下面依然是从这两个方面对本次的特效进行实现。

 

而在这之前,需要准备好一张水滴(水滴太多了也就成了水幕了)的效果图片(google“water drop”一下,稍微筛选一下就有了,最好是能找到或者自己加工成无缝衔接的),放置于我们特效的脚本实现文件目录附加的一个Resources的文件夹中,那么我们在脚本中适当的地方写上一句:

Texture2 = Resources.Load("ScreenWaterDrop")as Texture2D;

 

就可以读取到这张图片了。

浅墨准备的图片如下(无水印本图片的可以在浅墨的Github中找到,或者直接下文章末尾的项目工程)。

ScreenWaterDrop.png:


 

 


 

4.1 Shader实现部分

 


老规矩,先上详细注释的代码。

[cpp]  view plain  copy
 print ?
  1. //-----------------------------------------------【Shader脚本说明】---------------------------------------------------  
  2. //       屏幕水幕特效的实现代码-Shader脚本部分  
  3. //      2015年10月  Created by  浅墨  
  4. //      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo  
  5. //---------------------------------------------------------------------------------------------------------------------  
  6.   
  7. Shader "浅墨Shader编程/Volume9/ScreenWaterDropEffect"  
  8. {  
  9.     //------------------------------------【属性值】------------------------------------  
  10.     Properties  
  11.     {  
  12.         //主纹理  
  13.         _MainTex ("Base (RGB)", 2D) = "white" {}  
  14.         //屏幕水滴的素材图  
  15.         _ScreenWaterDropTex ("Base (RGB)", 2D) = "white" {}  
  16.         //当前时间  
  17.         _CurTime ("Time", Range(0.0, 1.0)) = 1.0  
  18.         //X坐标上的水滴尺寸  
  19.         _SizeX ("SizeX", Range(0.0, 1.0)) = 1.0  
  20.         //Y坐标上的水滴尺寸  
  21.         _SizeY ("SizeY", Range(0.0, 1.0)) = 1.0  
  22.         //水滴的流动速度  
  23.         _DropSpeed ("Speed", Range(0.0, 10.0)) = 1.0  
  24.         //溶解度  
  25.         _Distortion ("_Distortion", Range(0.0, 1.0)) = 0.87  
  26.     }  
  27.   
  28.     //------------------------------------【唯一的子着色器】------------------------------------  
  29.     SubShader  
  30.     {  
  31.         Pass  
  32.         {  
  33.             //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTest Off)  
  34.             ZTest Always  
  35.               
  36.             //===========开启CG着色器语言编写模块===========  
  37.             CGPROGRAM  
  38.   
  39.             //编译指令:告知编译器顶点和片段着色函数的名称  
  40.             #pragma vertex vert  
  41.             #pragma fragment frag  
  42.             #pragma fragmentoption ARB_precision_hint_fastest  
  43.             //编译指令: 指定着色器编译目标为Shader Model 3.0  
  44.             #pragma target 3.0  
  45.   
  46.             //包含辅助CG头文件  
  47.             #include "UnityCG.cginc"  
  48.   
  49.             //外部变量的声明  
  50.             uniform sampler2D _MainTex;  
  51.             uniform sampler2D _ScreenWaterDropTex;  
  52.             uniform float _CurTime;  
  53.             uniform float _DropSpeed;  
  54.             uniform float _SizeX;  
  55.             uniform float _SizeY;  
  56.             uniform float _Distortion;  
  57.             uniform float2 _MainTex_TexelSize;  
  58.               
  59.             //顶点输入结构  
  60.             struct vertexInput  
  61.             {  

你可能感兴趣的:(unity,shader,unity,unity3d)