Shader编程】之十四 边缘发光Shader(Rim Shader)的两种实现形态

本系列文章由@浅墨_毛星云 出品,转载请注明出处。  
文章链接: http://blog.csdn.net/poem_qianmo/article/details/51764028
作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442
本文工程使用的Unity3D版本: 5.2.1 



这篇文章主要讲解了如何在Unity3D中分别使用Surface Shader和Vertex & Fragment Shader来编写边缘发光Shader。

 



一、最终实现的效果

 


边缘发光Shader比较直观的一个运用便是模拟宇宙中的星球效果。将本文实现的边缘发光Shader先赋一个Material,此Material再赋给一个球体,加上Galaxy Skybox, 实现的效果如下图:

 


当然,边缘发光Shader也可以实现例如暗黑三中精英怪的高亮效果,将一个怪物模型本身的Shader用边缘发光Shader替代,实现效果如下图:

 

以下是本文实现的Shader在编辑器中的一些效果图。

        

   





二、Shader实现思路分析

 


思路方面,其实理解起来非常简单,就是在正常的漫反射Shader的基础之上,在最终的漫反射颜色值出来之后,再准备一个自发光颜色值,附加上去,即得到了最终的带自发光的颜色值。

按公式来表达,也就是这样:

 

最终颜色 = (漫反射系数 x 纹理颜色 x RGB颜色)+自发光颜色

 

按英文公式来表达,也就是这样:


FinalColor=(Diffuse x Texture x RGBColor)+Emissive

 

 

 

 

 

三、Surface Shader版边缘发光Shader实现



如果读过这个系列第一篇文章的朋友,应该还记得这个系列的第一篇文章(传送门:http://blog.csdn.net/poem_qianmo/article/details/40723789,里面的TheFirstShader,它就是一个标准的使用Unity Surface Shader实现的边缘发光Shader。

利用Unity自身的Shader封装,Surface Shader,也就是Shaderlab,算是新手或者想快速上手的童鞋学习Unity中Shader书写的一个非常好的切入点。

这边贴出经过加强的第一期TheFirstShader详细注释后的源代码。可以发现里面关于主要框架的注释都是中英双语的,因为发现不少国外友人也关注了我在Github上的Shader repo(https://github.com/QianMo/Awesome-Unity-Shader),为了方便他们以及后面更多的国外友人,以后如果周末写博客的时间充裕,就干脆写中英双语的注释得了。

OK,详细注释后的Surface Shader版可发光Shader源代码如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Shader "Learning Unity Shader/Lecture 14/Surface Rim Shader"  
  2. {  
  3.     //-----------------------------------【属性 || Properties】------------------------------------------    
  4.     Properties  
  5.     {  
  6.         //主颜色 || Main Color  
  7.         _MainColor("【主颜色】Main Color", Color) = (0.5,0.5,0.5,1)  
  8.         //漫反射纹理 || Diffuse Texture  
  9.         _MainTex("【纹理】Texture", 2D) = "white" {}  
  10.         //凹凸纹理 || Bump Texture  
  11.         _BumpMap("【凹凸纹理】Bumpmap", 2D) = "bump" {}  
  12.         //边缘发光颜色 || Rim Color  
  13.         _RimColor("【边缘发光颜色】Rim Color", Color) = (0.17,0.36,0.81,0.0)  
  14.         //边缘发光强度 ||Rim Power  
  15.         _RimPower("【边缘颜色强度】Rim Power", Range(0.6,36.0)) = 8.0  
  16.         //边缘发光强度系数 || Rim Intensity Factor  
  17.         _RimIntensity("【边缘颜色强度系数】Rim Intensity", Range(0.0,100.0)) = 1.0  
  18.     }  
  19.   
  20.     //----------------------------------【子着色器 || SubShader】---------------------------------------    
  21.     SubShader  
  22.     {  
  23.         //渲染类型为Opaque,不透明 || RenderType Opaque  
  24.         Tags  
  25.         {  
  26.             "RenderType" = "Opaque"   
  27.         }  
  28.   
  29.         //-------------------------开启CG着色器编程语言段 || Begin CG Programming Part----------------------   
  30.         CGPROGRAM  
  31.   
  32.             //【1】声明使用兰伯特光照模式 ||Using the Lambert light mode  
  33.             #pragma surface surf Lambert    
  34.   
  35.             //【2】定义输入结构 ||  Input Struct  
  36.             struct Input  
  37.             {  
  38.                 //纹理贴图 || Texture  
  39.                 float2 uv_MainTex;  
  40.                 //法线贴图 || Bump Texture  
  41.                 float2 uv_BumpMap;  
  42.                 //观察方向 || Observation direction  
  43.                 float3 viewDir;    
  44.             };  
  45.   
  46.             //【3】变量声明 || Variable Declaration  
  47.             //边缘颜色  
  48.             float4 _MainColor;  
  49.             //主纹理  
  50.             sampler2D _MainTex;    
  51.             //凹凸纹理    
  52.             sampler2D _BumpMap;  
  53.             //边缘颜色  
  54.             float4 _RimColor;  
  55.             //边缘颜色强度  
  56.             float _RimPower;  
  57.             //边缘颜色强度  
  58.             float _RimIntensity;  
  59.   
  60.             //【4】表面着色函数的编写 || Writing the surface shader function  
  61.             void surf(Input IN, inout SurfaceOutput o)  
  62.             {  
  63.                 //表面反射颜色为纹理颜色    
  64.                 o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb*_MainColor.rgb;  
  65.                 //表面法线为凹凸纹理的颜色    
  66.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));  
  67.                 //边缘颜色    
  68.                 half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));  
  69.                 //计算出边缘颜色强度系数    
  70.                 o.Emission = _RimColor.rgb * pow(rim, _RimPower)*_RimIntensity;  
  71.             }  
  72.   
  73.         //-------------------结束CG着色器编程语言段 || End CG Programming Part------------------    
  74.         ENDCG  
  75.     }  
  76.   
  77.         //后备着色器为普通漫反射 || Fallback use Diffuse  
  78.         Fallback "Diffuse"  
  79. }  

 

稍微琢磨一下就明白,此Shader其实就是用利用了Unity5中封装好的Standard Surface Output结构体中的Emission(自发光)属性,来达到这样的边缘光效果,技术含量很有限。这边附一下Unity5中的SurfaceOutputStandard原型:

 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // Unity5 SurfaceOutputStandard原型:  
  2. struct SurfaceOutputStandard  
  3. {  
  4.     fixed3 Albedo;                  // 漫反射颜色  
  5.     fixed3 Normal;                  // 切线空间法线  
  6.     half3 Emission;                 //自发光  
  7.     half Metallic;                    // 金属度;取0为非金属, 取1为金属  
  8.     half Smoothness;             // 光泽度;取0为非常粗糙, 取1为非常光滑  
  9.     half Occlusion;                 // 遮挡(默认值为1)  
  10.     fixed Alpha;                      // 透明度  
  11. };  


将此Shader赋给Material后在编辑器效果图:




 

 里面有6个参数,包括主颜色、纹理、凹凸纹理、边缘发光颜色、边缘颜色强度、边缘颜色强度系数这六个参数可以定制与调节,只要贴图资源到位,就很容易可以调出自己满意的效果来。



 

 


四、可编程Shader版边缘发光Shader的实现



这篇文章核心主要就是实现本节的这个可编程版(也就是Vertex & Fragment Shader)边缘发光Shader。大家知道,Vertex & Fragment Shader是比Surface Shader更高一段位的实现形态,有更大的可控性,更好的可编程性,可以实现更加丰富的效果,是更贴近CG着色语言的一种Shader形态。

OK,直接贴出经过详细注释的Vertex & Fragment Shader版边缘发光Shader实现源代码:

 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Shader "Learning Unity Shader/Lecture 14/Basic Rim Shader"   
  2. {  
  3.     //-----------------------------------【属性 || Properties】------------------------------------------    
  4.     Properties  
  5.     {  
  6.         //主颜色 || Main Color  
  7.         _MainColor("【主颜色】Main Color", Color) = (0.5,0.5,0.5,1)  
  8.         //漫反射纹理 || Diffuse Texture  
  9.         _TextureDiffuse("【漫反射纹理】Texture Diffuse", 2D) = "white" {}    
  10.         //边缘发光颜色 || Rim Color  
  11.         _RimColor("【边缘发光颜色】Rim Color", Color) = (0.5,0.5,0.5,1)  
  12.         //边缘发光强度 ||Rim Power  
  13.         _RimPower("【边缘发光强度】Rim Power", Range(0.0, 36)) = 0.1  
  14.         //边缘发光强度系数 || Rim Intensity Factor  
  15.         _RimIntensity("【边缘发光强度系数】Rim Intensity", Range(0.0, 100)) = 3  
  16.     }  
  17.   
  18.     //----------------------------------【子着色器 || SubShader】---------------------------------------    
  19.     SubShader  
  20.     {  
  21.         //渲染类型为Opaque,不透明 || RenderType Opaque  
  22.         Tags  
  23.         {  
  24.             "RenderType" = "Opaque"  
  25.         }  
  26.   
  27.         //---------------------------------------【唯一的通道 || Pass】------------------------------------  
  28.         Pass  
  29.         {  
  30.             //设定通道名称 || Set Pass Name  
  31.             Name "ForwardBase"  
  32.   
  33.             //设置光照模式 || LightMode ForwardBase  
  34.             Tags  
  35.             {  
  36.                 "LightMode" = "ForwardBase"  
  37.             }  
  38.   
  39.             //-------------------------开启CG着色器编程语言段 || Begin CG Programming Part----------------------    
  40.             CGPROGRAM  
  41.   
  42.                 //【1】指定顶点和片段着色函数名称 || Set the name of vertex and fragment shader function  
  43.                 #pragma vertex vert  
  44.                 #pragma fragment frag  
  45.   
  46.                 //【2】头文件包含 || include  
  47.                 #include "UnityCG.cginc"  
  48.                 #include "AutoLight.cginc"  
  49.   
  50.                 //【3】指定Shader Model 3.0 || Set Shader Model 3.0  
  51.                 #pragma target 3.0  
  52.   
  53.                 //【4】变量声明 || Variable Declaration  
  54.                 //系统光照颜色  
  55.                 uniform float4 _LightColor0;  
  56.                 //主颜色  
  57.                 uniform float4 _MainColor;  
  58.                 //漫反射纹理  
  59.                 uniform sampler2D _TextureDiffuse;   
  60.                 //漫反射纹理_ST后缀版  
  61.                 uniform float4 _TextureDiffuse_ST;  
  62.                 //边缘光颜色  
  63.                 uniform float4 _RimColor;  
  64.                 //边缘光强度  
  65.                 uniform float _RimPower;  
  66.                 //边缘光强度系数  
  67.                 uniform float _RimIntensity;  
  68.   
  69.                 //【5】顶点输入结构体 || Vertex Input Struct  
  70.                 struct VertexInput   
  71.                 {  
  72.                     //顶点位置 || Vertex position  
  73.                     float4 vertex : POSITION;  
  74.                     //法线向量坐标 || Normal vector coordinates  
  75.                     float3 normal : NORMAL;  
  76.                     //一级纹理坐标 || Primary texture coordinates  
  77.                     float4 texcoord : TEXCOORD0;  
  78.                 };  
  79.   
  80.                 //【6】顶点输出结构体 || Vertex Output Struct  
  81.                 struct VertexOutput   
  82.                 {  
  83.                     //像素位置 || Pixel position  
  84.                     float4 pos : SV_POSITION;  
  85.                     //一级纹理坐标 || Primary texture coordinates  
  86.                     float4 texcoord : TEXCOORD0;  
  87.                     //法线向量坐标 || Normal vector coordinates  
  88.                     float3 normal : NORMAL;  
  89.                     //世界空间中的坐标位置 || Coordinate position in world space  
  90.                     float4 posWorld : TEXCOORD1;  
  91.                     //创建光源坐标,用于内置的光照 || Function in AutoLight.cginc to create light coordinates  
  92.                     LIGHTING_COORDS(3,4)  
  93.                 };  
  94.   
  95.                 //【7】顶点着色函数 || Vertex Shader Function  
  96.                 VertexOutput vert(VertexInput v)   
  97.                 {  
  98.                     //【1】声明一个顶点输出结构对象 || Declares a vertex output structure object  
  99.                     VertexOutput o;  
  100.   
  101.                     //【2】填充此输出结构 || Fill the output structure  
  102.                     //将输入纹理坐标赋值给输出纹理坐标  
  103.                     o.texcoord = v.texcoord;  
  104.                     //获取顶点在世界空间中的法线向量坐标    
  105.                     o.normal = mul(float4(v.normal,0), _World2Object).xyz;  
  106.                     //获得顶点在世界空间中的位置坐标    
  107.                     o.posWorld = mul(_Object2World, v.vertex);  
  108.                     //获取像素位置  
  109.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
  110.   
  111.                     //【3】返回此输出结构对象  || Returns the output structure  
  112.                     return o;  
  113.                 }  
  114.   
  115.                 //【8】片段着色函数 || Fragment Shader Function  
  116.                 fixed4 frag(VertexOutput i) : COLOR  
  117.                 {  
  118.                     //【8.1】方向参数准备 || Direction  
  119.                     //视角方向  
  120.                     float3 ViewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);  
  121.                     //法线方向  
  122.                     float3 Normalection = normalize(i.normal);  
  123.                     //光照方向  
  124.                     float3 LightDirection = normalize(_WorldSpaceLightPos0.xyz);  
  125.   
  126.                     //【8.2】计算光照的衰减 || Lighting attenuation  
  127.                     //衰减值  
  128.                     float Attenuation = LIGHT_ATTENUATION(i);  
  129.                     //衰减后颜色值  
  130.                     float3 AttenColor = Attenuation * _LightColor0.xyz;  
  131.   
  132.                     //【8.3】计算漫反射 || Diffuse  
  133.                     float NdotL = dot(Normalection, LightDirection);  
  134.                     float3 Diffuse = max(0.0, NdotL) * AttenColor + UNITY_LIGHTMODEL_AMBIENT.xyz;  
  135.   
  136.                     //【8.4】准备自发光参数 || Emissive  
  137.                     //计算边缘强度  
  138.                     half Rim = 1.0 - max(0, dot(i.normal, ViewDirection));  
  139.                     //计算出边缘自发光强度  
  140.                     float3 Emissive = _RimColor.rgb * pow(Rim,_RimPower) *_RimIntensity;  
  141.   
  142.                     //【8.5】计在最终颜色中加入自发光颜色 || Calculate the final color  
  143.                     //最终颜色 = (漫反射系数 x 纹理颜色 x rgb颜色)+自发光颜色 || Final Color=(Diffuse x Texture x rgbColor)+Emissive  
  144.                     float3 finalColor = Diffuse * (tex2D(_TextureDiffuse,TRANSFORM_TEX(i.texcoord.rg, _TextureDiffuse)).rgb*_MainColor.rgb) + Emissive;  
  145.                   
  146.                     //【8.6】返回最终颜色 || Return final color  
  147.                     return fixed4(finalColor,1);  
  148.                 }  
  149.   
  150.             //-------------------结束CG着色器编程语言段 || End CG Programming Part------------------    
  151.             ENDCG  
  152.         }  
  153.     }  
  154.   
  155.     //后备着色器为普通漫反射 || Fallback use Diffuse  
  156.     FallBack "Diffuse"  
  157. }  

 相信不少朋友已经看出来了,与普通的漫反射Shader相比,这个Shader的魔力就在于多出了“8.4准备自发光参数”和“8.5在最终颜色中加入自发光颜色"两个步骤而已,前面都是普通的Vertex & Fragment Shader常规写法。


将此Shader赋给Material,得到的效果如下:

  

 

  


当然,你也可以将这两个Shader用于场景中各种模型,以下是一组效果图:

 









OK,这篇文章的内容大致如此。我们下篇文章,再会。




附: 本文配套源码下载链接


【Github】本文Shader源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值