(上)Unity5新版Shader模板源码解析&运动模糊屏幕特效...
Shader材质球TA图形学渲染引擎 8522 70
实名

通过了实名认证的内容创造者

发布于 2015-10-28 10:01:32

您需要 登录 才可以下载或查看,没有账号?注册

x
【已获得授权转载】
原帖地址见末尾:(注意。原本叫长。分上下篇发。下篇地址:【元素推荐】(下)Unity5新版Shader模板源码解析&运动模糊屏幕特效的实现
        本文工程使用的Unity3D版本: 5.2.1
         
           概要:本文对Unity5中全新的三种Shader模板的源码进行了解析,然后还讲解了运动模糊屏幕特效的实现方法。


前言


时隔9个月,终于有了一些稍微空闲的时间,可以进行一些更新了。
鉴于以后可以用来写博客的时间肯定不会非常充裕,个人觉得再讲Shader的基础写法比较拖节奏,所以最好是把力气用在刀刃上,在有时间更新的时候,主要讲一些更加前沿、实用的技巧,以及进阶一些的Shader的写法。

另外,从本次开始,每次更新的标题也就不取那么主题化了(比如之前的暗黑城堡、静谧之秋之类的)。人老了,渐渐文艺不起来了,还是实在一点好。:D

好的,就交代这么多。接下来就按新的风格试着写几次。。

看一组程序截图之后,便开始我们的正文。如下图,一份运动模糊屏幕特效的效果对比。

原始场景:
1.jpg

开启运动模糊效果后:
2.jpg

图先就上这两张。文章末尾有更多的运行截图,并提供了源工程的下载。
正文内容:
一、Unity5中新的Shader体系简析


Unity5和之前的书写模式有了一定的改变。Unity5时代的Shader Reference官方文档也进一步地变得丰满。
主要需要了解到的是,在原来的Unity中,若想要新建一个Shader源文件,不考虑compute shader的话,仅有一种Shader模板供选择。而自从Unity5.2起
想在Unity5.2之后的版本中新建Shader,【右键在Project窗口中单击】->【Create】,会出现如下的四个选项:



而由于暂时不考虑compute shader。所以,新版Unity中有三种基本的Shader模板分别为:
Standard Surface Shader标准表面着色器
Unlit Shader 无灯光着色器
Image Effect Shader 图像特效着色器




二、Unity5中新的Shader模板源码解析

下面,对Unity5中三种基本Shader模板进行逐行注释与思路解析。
可以点击
这里跳转到Github,查看详细注释好的三种Shader模板的源码。

2.1 标准表面着色器(Standard Surface Shader)模板源码解析

在Unity中,我们若要实现新的表面着色器时,可以根据这个模板,进行一步添加子着色器和新的参数与特性。
这个Shader模板的脉络很清晰,先是定义一些属性,然后在SubShader中设置渲染模式,层次细节LOD的值,然后开启一个CG编程语言模块,写一些编译指令#pragma,声明一下变量让属性值在CG块中可见,定义输入结构,然后填充一下表面着色函数即可。
注意:专门强调一句,SurfaceShader不能使用Pass,一使用就报错,我们直接在SubShader中实现和填充代码就可以了。
Standard Surface Shader模板详细注释的Shader代码如下:
  1. Shader "浅墨Shader编程/Volume8/Surface Shader模板"
  2. {
  3.        //------------------------------------【属性值】------------------------------------
  4.        Properties
  5.        {
  6.               //主颜色
  7.               _Color("Color", Color) = (1,1,1,1)
  8.               //主纹理
  9.               _MainTex("Albedo (RGB)", 2D) = "white" {}
  10.               //光泽度
  11.               _Glossiness("Smoothness", Range(0,1)) = 0.5
  12.               //金属度
  13.               _Metallic("Metallic", Range(0,1)) = 0.0
  14.        }

  15.        //------------------------------------【唯一的子着色器】------------------------------------
  16.        SubShader
  17.        {
  18.               //【注意:Surface Shader不能使用Pass,直接在SubShader中实现即可】

  19.               //渲染类型设置:不透明
  20.               Tags{"RenderType" = "Opaque" }

  21.               //细节层次设为:200
  22.               LOD 200

  23.               //===========开启CG着色器语言编写模块===========
  24.               CGPROGRAM

  25.               //编译指令:告知编译器表明着色函数的名称为surf
  26.               //Standard表示光照模型为Unity标准版光照模型
  27.               //fullforwardshadows表示在正向渲染路径中支持所有阴影类型
  28.               #pragma surface surf Standard fullforwardshadows

  29.               //编译指令: 指定着色器编译目标为Shader Model 3.0
  30.               #pragma target 3.0

  31.               //变量的声明
  32.               sampler2D_MainTex;

  33.               //表面输入结构体
  34.               struct Input
  35.               {
  36.                      float2 uv_MainTex;//纹理坐标
  37.               };

  38.               //变量的声明
  39.               half_Glossiness;
  40.               half_Metallic;
  41.               fixed4_Color;

  42.               //--------------------------------【表面着色函数】-----------------------------
  43.               //输入:表面输入结构体
  44.               //输出:Unity内置的SurfaceOutputStandard结构体
  45.               //SurfaceOutputStandard原型如下:
  46.               /*
  47.                      struct SurfaceOutputStandard
  48.                      {
  49.                             fixed3 Albedo;                  // 漫反射颜色
  50.                             fixed3 Normal;                  // 切线空间法线
  51.                             half3 Emission;                 //自发光
  52.                             half Metallic;                           // 金属度;取0为非金属, 取1为金属
  53.                             half Smoothness;             // 光泽度;取0为非常粗糙, 取1为非常光滑
  54.                             half Occlusion;                 // 遮挡(默认值为1)
  55.                             fixed Alpha;                      // 透明度
  56.                      };
  57.               */
  58.               //---------------------------------------------------------------------------------
  59.               void surf(Input IN, inout SurfaceOutputStandard o)
  60.               {
  61.                      //【1】漫反射颜色为主纹理对应的纹理坐标,并乘以主颜色
  62.                      fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
  63.                      //【2】将准备好的颜色的rgb分量作为漫反射颜色
  64.                      o.Albedo= c.rgb;
  65.                      //【3】金属度取自属性值
  66.                      o.Metallic= _Metallic;
  67.                      //【4】光泽度也取自属性值
  68.                      o.Smoothness= _Glossiness;
  69.                      //【5】将准备好的颜色的alpha分量作为Alpha分量值
  70.                      o.Alpha= c.a;
  71.               }

  72.               //===========结束CG着色器语言编写模块===========
  73.               ENDCG
  74.        }
  75.        //备胎为漫反射
  76.        FallBack"Diffuse"
  77. }
点击此处复制文本

2.2 无灯光着色器(Unlit Shader)模板源码解析

Unlit Shader,简单来说,就是直接采用漫反射纹理,不考虑场景中的任何灯光效果。使用无灯光着色器的话,也就不能使用任何镜面或者法线效果了。Unlit系的Shader基本原理和其他Shader无异,但是计算量更小,更快速,更高效。
而在Unity内置的各种着色器中,有如下的四种是Unlit系的:


好的,已经稍微解释了下什么是Unlit Shader。下面一起看一下Unity为我们提供的无灯光着色器模板的代码:
  1. Shader "浅墨Shader编程/Volume8/无灯光着色器(Unlit Shader)模板"
  2. {  
  3.        //------------------------------------【属性值】------------------------------------  
  4.        Properties  
  5.        {  
  6.               //主纹理  
  7.               _MainTex("Texture", 2D) = "white" {}  
  8.        }  
  9.    
  10.        //------------------------------------【唯一的子着色器】------------------------------------  
  11.        SubShader  
  12.        {  
  13.               //渲染类型设置:不透明  
  14.               Tags{ "RenderType"="Opaque" }  
  15.    
  16.               //细节层次设为:200  
  17.               LOD100  
  18.    
  19.               //--------------------------------唯一的通道-------------------------------  
  20.               Pass  
  21.               {  
  22.                      //===========开启CG着色器语言编写模块===========  
  23.                      CGPROGRAM  
  24.    
  25.                      //编译指令:告知编译器顶点和片段着色函数的名称  
  26.                      #pragma vertex vert  
  27.                      #pragma fragment frag  
  28.    
  29.                      //着色器变体快捷编译指令:雾效。编译出几个不同的Shader变体来处理不同类型的雾效(关闭/线性/指数/二阶指数)  
  30.                      #pragma multi_compile_fog  
  31.    
  32.                      //包含头文件  
  33.                      #include "UnityCG.cginc"  
  34.    
  35.                      //顶点着色器输入结构  
  36.                      struct appdata  
  37.                      {  
  38.                             float4 vertex : POSITION;//顶点位置  
  39.                             float2 uv : TEXCOORD0;//纹理坐标  
  40.                      };  
  41.    
  42.                      //顶点着色器输出结构  
  43.                      struct v2f  
  44.                      {  
  45.                             float2 uv : TEXCOORD0;//纹理坐标  
  46.                             UNITY_FOG_COORDS(1)//雾数据  
  47.                             float4 vertex : SV_POSITION;//像素位置  
  48.                      };  
  49.    
  50.                      //变量声明  
  51.                      sampler2D _MainTex;  
  52.                      float4 _MainTex_ST;  
  53.                        
  54.                      //--------------------------------【顶点着色函数】-----------------------------  
  55.                      //输入:顶点输入结构体  
  56.                      //输出:顶点输出结构体  
  57.                      //---------------------------------------------------------------------------------  
  58.                      v2f vert (appdata v)  
  59.                      {  
  60.                             //【1】实例化一个输入结构体  
  61.                             v2f o;  
  62.                             //【2】填充此输出结构  
  63.                             //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  64.                             o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);  
  65.                             //【3】用UnityCG.cginc头文件中内置定义的宏,根据uv坐标来计算真正的纹理上对应的位置(按比例进行二维变换)                  
  66.                             o.uv= TRANSFORM_TEX(v.uv, _MainTex);  
  67.                             //【4】用UnityCG.cginc头文件中内置定义的宏处理雾效,从顶点着色器中输出雾效数据  
  68.                             UNITY_TRANSFER_FOG(o,o.vertex);  
  69.    
  70.                             //【5】返回此输出结构对象  
  71.                             return o;  
  72.                      }  
  73.                        
  74.                      //--------------------------------【片段着色函数】-----------------------------  
  75.                      //输入:顶点输出结构体  
  76.                      //输出:float4型的像素颜色值  
  77.                      //---------------------------------------------------------------------------------  
  78.                      fixed4 frag (v2f i) : SV_Target  
  79.                      {  
  80.                             //【1】采样主纹理在对应坐标下的颜色值  
  81.                             fixed4 col = tex2D(_MainTex, i.uv);  
  82.    
  83.                             //【2】用UnityCG.cginc头文件中内置定义的宏启用雾效  
  84.                             UNITY_APPLY_FOG(i.fogCoord,col);            
  85.    
  86.                             //【3】返回最终的颜色值  
  87.                             return col;  
  88.                      }  
  89.    
  90.                      //===========结束CG着色器语言编写模块===========  
  91.                      ENDCG  
  92.               }  
  93.        }  
  94. }
点击此处复制文本


不难分析得到,无灯光着色器是一种顶点&片段着色器,这边模板给出的是单子着色器,单通道的写法。

并且,无灯光着色器中使用了一些UnityCG.cginc头文件中内置的宏,

比如说TRANSFORM_TEX、UNITY_TRANSFER_FOG、UNITY_APPLY_FOG。接下来分别把这三个宏简单解释一下。

2.2.1 TRANSFORM_TEX宏

TRANSFORM_TEX宏的定义为:
  1. #define TRANSFORM_TEX(tex,name) (tex.xy *name##_ST.xy + name##_ST.zw)
点击此处复制文本
其位于UnityCG.cginc(Unity5.2.1版本)的第266行。
其可以根据uv坐标来计算真正的纹理上对应的位置(按比例进行二维变换),组合上上文中定义的float4 _MainTex_ST,便可以计算真正的纹理上对应的位置。

2.2.2 UNITY_TRANSFER_FOG宏

UNITY_TRANSFER_FOG宏的作用是从顶点着色输出雾数据。在UnityCG.cginc(Unity5.2.1版本)的第772行起,具体定义如下:
  1. #if (SHADER_TARGET < 30) ||defined(SHADER_API_MOBILE)  
  2.               //手机端或者Shader Mode 2.0: 计算每个顶点的雾效因子  
  3.               #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord =unityFogFactor  
  4.        #else  
  5.               //Shader Mode 3.0和PC和游戏机: 计算每像素的雾距离,和每像素的雾效因子  
  6.               #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord = (outpos).z  
  7.        #endif
点击此处复制文本

2.2.3 UNITY_APPLY_FOG宏

UNITY_APPLY_FOG宏的定义稍微有些长,从UnityCG.cginc(Unity 5.2.1版本)的第787行起:

  1. #if defined(FOG_LINEAR) || defined(FOG_EXP)|| defined(FOG_EXP2)  
  2.        #if(SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)  
  3.               //mobile or SM2.0: fog factor was already calculated per-vertex, so just lerp thecolor  
  4.               #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_FOG_LERP_COLOR(col,fogCol,coord)  
  5.        #else  
  6.               //SM3.0 and PC/console: calculate fog factor and lerp fog color  
  7.               #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_CALC_FOG_FACTOR(coord);UNITY_FOG_LERP_COLOR(col,fogCol,unityFogFactor)  
  8.        #endif  
  9. #else  
  10.        #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol)  
  11. #endif  
  12.    
  13. #ifdef UNITY_PASS_FORWARDADD  
  14.        #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0))  
  15. #else  
  16.        #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor)  
  17. #endif
点击此处复制文本
可以发现,UNITY_APPLY_FOG宏的作用是从顶点着色器中输出雾效数据,将第二个参数中的颜色值作为雾效的颜色值,且在正向附加渲染通道(forward-additive pass)中,
自动设置纯黑色(fixed4(0,0,0,0))的雾效。其在定义中借助了UNITY_APPLY_FOG_COLOR宏,
而我们也可以使用UNITY_APPLY_FOG_COLOR来指定特定颜色的雾效。

2.3 图像特效着色器(Image Effect Shader) 模板源码解析

这里的图像特效一般指的就是屏幕图像特效,在Camera加上各种滤镜,
比如说屏幕溅血,像素化,色调的调整,画面模糊等效果。其也是一个顶点&片段着色器,
且一般主要的操作集中在片段着色函数中。Unity为我们提供的模板,经过详细注释后的源码如下:
  1. Shader "浅墨Shader编程/Volume8/图像特效Shader模板"
  2. {  
  3.        //------------------------------------【属性值】------------------------------------  
  4.        Properties  
  5.        {  
  6.               //主纹理  
  7.               _MainTex("Texture", 2D) = "white" {}  
  8.        }  
  9.    
  10.        //------------------------------------【唯一的子着色器】------------------------------------  
  11.        SubShader  
  12.        {  
  13.               //关闭剔除操作  
  14.               Cull Off  
  15.               //关闭深度写入模式  
  16.               ZWrite Off  
  17.               //设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTestOff)  
  18.               ZTest Always  
  19.    
  20.               //--------------------------------唯一的通道-------------------------------  
  21.               Pass  
  22.               {  
  23.                      //===========开启CG着色器语言编写模块===========  
  24.                      CGPROGRAM  
  25.    
  26.                      //编译指令:告知编译器顶点和片段着色函数的名称  
  27.                      #pragma vertex vert  
  28.                      #pragma fragment frag  
  29.                        
  30.                      //包含头文件  
  31.                      #include "UnityCG.cginc"  
  32.    
  33.                      //顶点着色器输入结构  
  34.                      struct appdata  
  35.                      {  
  36.                             float4 vertex : POSITION;//顶点位置  
  37.                             float2 uv : TEXCOORD0;//一级纹理坐标  
  38.                      };  
  39.    
  40.                      //顶点着色器输出结构(v2f,vertex to fragment)  
  41.                      struct v2f  
  42.                      {  
  43.                             float2 uv : TEXCOORD0;//一级纹理坐标  
  44.                             float4 vertex : SV_POSITION;//像素位置  
  45.                      };  
  46.    
  47.                      //--------------------------------【顶点着色函数】-----------------------------  
  48.                      //输入:顶点输入结构体  
  49.                      //输出:顶点输出结构体  
  50.                      //---------------------------------------------------------------------------------  
  51.                      //顶点着色函数  
  52.                      v2f vert (appdata v)  
  53.                      {  
  54.                             //【1】实例化一个输入结构体  
  55.                             v2f o;  
  56.    
  57.                             //【2】填充此输出结构  
  58.                             //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  59.                             o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);  
  60.                             //输入的UV纹理坐标为顶点输出的坐标  
  61.                             o.uv= v.uv;  
  62.    
  63.                             //【3】返回此输出结构对象  
  64.                             return o;  
  65.                      }  
  66.    
  67.                      //变量的声明  
  68.                      sampler2D _MainTex;  
  69.    
  70.                      //--------------------------------【片段着色函数】-----------------------------  
  71.                      //输入:顶点输出结构体  
  72.                      //输出:float4型的像素颜色值  
  73.                      //---------------------------------------------------------------------------------  
  74.                      fixed4 frag (v2f i) : SV_Target  
  75.                      {  
  76.                             //【1】采样主纹理在对应坐标下的颜色值  
  77.                             fixed4 col = tex2D(_MainTex, i.uv);  
  78.                             //【2】将颜色值反向  
  79.                             col= 1 - col;  
  80.    
  81.                             //【3】返回最终的颜色值  
  82.                             return col;  
  83.                      }  
  84.    
  85.                      //===========结束CG着色器语言编写模块===========  
  86.                      ENDCG  
  87.               }  
  88.        }  
  89. }
点击此处复制文本



2.4 Shader模板中文注释格式调整版替换

其实可以将Unity5中自带的上述三个着色器模板,替换成上文中贴出来的、经过详细注释和格式调整的Shader模板,这样在每次新建Shader时,就已经得到了具有很高可读性的Shader模板了,非常便捷。


一定要吐槽的是,Unity5.2.1自带的三个Shader模板的缩进和空格完全是混用的,导致在通过他们新建出来的Shader里面写代码的时候,格式非常混乱,十分影响新版Unity中Shader的编码体验。很明显,准备此Shader模板的Unity开发人员的编码习惯有点欠缺,得在这里点名批评,轻喷一下。


浅墨在一发现他们格式有问题的时候就马上替换掉了,所以现在在Unity中写Shader代码的体验是非常棒的。这边也教大家如何替换掉自带的3个模板。
Unity中Shader模板的位置是…Unity\Editor\Data\Resources\ScriptTemplates,比如说Unity安装在D:\ProgramFiles\路径下,整体路径就是:
D:\ProgramFiles\Unity\Editor\Data\Resources\ScriptTemplates。


在此路径下的3个txt,即为对应的三个Shader模板文件:
83-Shader__Standard SurfaceShader-NewSurfaceShader.shader.txt
84-Shader__UnlitShader-NewUnlitShader.shader.txt
85-Shader__Image EffectShader-NewImageEffectShader.shader


这边已经将调整好格式,详细注释的三种模板准备好了,下载之后,找到上面提到的…Unity\Editor\Data\Resources\ScriptTemplates目录。替换掉对应的3个txt文件即可。需要注意的是,如果你想自己DIY Shader模板,需要将txt保存为UTF-8编码格式,否则可能会出现乱码。


另外还有一个小细节可以提一下。如果你安装了两个或者两个以上的Unity5.1之后版本的Unity,如果你替换你当前使用的Unity路径下的模板文件后,新建的模板文件没有改变的话,你试着将所有的Unity5.1之后版本的路径下的这三个模板文件都进行替换,应该就可以实现想要的替换效果。浅墨的机器上就是同时存在Unity5.2.1和Unity5.2.0,然后使用Unity5.2.1,替换掉Unity5.2.1路径下的三个模板文件后,并没有发生变换。之后我按图索骥,替换了Unity 5.2.0版路径下的三个模板文化,才使得替换的模板文件生效。这估计是Unity多版本共存时,自身的一个小bug。




替换的模板以及试玩地址回复可见:


尊敬的游客,如果您要查看本帖关注 或 回复可见内容请关注回复后刷新页面查看!


本系列文章由@浅墨_毛星云 出品
        文章链接: http://blog.csdn.net/poem_qianmo/article/details/49405909
        作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442        
        




评分

参与人数 1元素币 +30 活跃度 +20 展开 理由
元素界王神... + 30 + 20

查看全部评分

本帖被以下画板推荐:

还没有设置签名!您可以在此展示你的链接,或者个人主页!
使用道具 <
GSY1021132574  发表于 2015-10-28 10:13:01  
2#
天下武功出少林,世界资源入元素!
回复 收起回复
使用道具
南瓜皮  发表于 2015-10-28 12:02:52  
3#
元素那么大,我想来看看!
回复 收起回复
使用道具
小黄  发表于 2015-10-28 12:05:58  
4#
天下武功出少林,世界资源入元素!
回复 收起回复
使用道具
野得像驴  发表于 2015-10-28 13:52:39  
5#
正是本尊想要的!
回复 收起回复
使用道具
changshen  发表于 2015-10-28 21:50:36  
6#
感谢分享了 不错
回复 收起回复
使用道具
野得像驴  发表于 2015-10-29 00:04:34  
7#
这个简直好的不要不要的
回复 收起回复
使用道具
正雨2015  发表于 2015-10-29 08:37:12  
8#
{:1_149:}
回复 收起回复
使用道具
不放弃  发表于 2015-10-29 09:51:45  
10#
元素那么大,我想来看看!
回复 收起回复
使用道具
maoyaohau123  发表于 2015-10-30 09:26:19  
11#
天下武功出少林,世界资源入元素!
回复 收起回复
使用道具
呃......  发表于 2015-11-2 11:30:20  
12#
这个屌,先收了。。。。。
回复 收起回复
使用道具
春山山  发表于 2015-11-3 16:21:03  
13#
好强!!!!!
回复 收起回复
使用道具
大头熊  发表于 2015-11-19 10:59:54  
14#
多谢楼主分享
回复 收起回复
使用道具
wyq0311  发表于 2015-11-19 17:24:57  
15#
资源发布哪家强?元素首发称大王
回复 收起回复
使用道具
Dragon_DDD  发表于 2015-11-23 15:11:09  
16#
66666,来看看,虽然还没接触到shader编程
回复 收起回复
使用道具
b8601166  发表于 2015-11-25 14:23:36  
17#
这个非常好,非常感谢
回复 收起回复
使用道具
幸福YY兔  发表于 2015-12-23 13:57:00  
19#
{:1_146:}
回复 收起回复
使用道具
红烧大白菜  发表于 2015-12-30 21:32:17  
20#
好东西,分享了哦也
回复 收起回复
使用道具
1234下一页
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表