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

  • Post author:
  • Post category:其他





本系列文章由



@浅墨_毛星云



出品,转载请注明出处。















文章链接:





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源代码如下:




  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. //———————————-【子着色器 || SubShader】—————————————




  20. SubShader

  21. {


  22. //渲染类型为Opaque,不透明 || RenderType Opaque




  23. Tags

  24. {


  25. “RenderType”


    =


    “Opaque”




  26. }



  27. //————————-开启CG着色器编程语言段 || Begin CG Programming Part———————-




  28. CGPROGRAM



  29. //【1】声明使用兰伯特光照模式 ||Using the Lambert light mode





  30. #pragma surface surf Lambert






  31. //【2】定义输入结构 ||  Input Struct





  32. struct


    Input


  33. {


  34. //纹理贴图 || Texture




  35. float2 uv_MainTex;


  36. //法线贴图 || Bump Texture




  37. float2 uv_BumpMap;


  38. //观察方向 || Observation direction




  39. float3 viewDir;

  40. };



  41. //【3】变量声明 || Variable Declaration





  42. //边缘颜色




  43. float4 _MainColor;


  44. //主纹理




  45. sampler2D _MainTex;


  46. //凹凸纹理




  47. sampler2D _BumpMap;


  48. //边缘颜色




  49. float4 _RimColor;


  50. //边缘颜色强度





  51. float


    _RimPower;



  52. //边缘颜色强度





  53. float


    _RimIntensity;




  54. //【4】表面着色函数的编写 || Writing the surface shader function





  55. void


    surf(Input IN, inout SurfaceOutput o)


  56. {


  57. //表面反射颜色为纹理颜色




  58. o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb*_MainColor.rgb;


  59. //表面法线为凹凸纹理的颜色




  60. o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));


  61. //边缘颜色




  62. half rim = 1.0 – saturate(dot(normalize(IN.viewDir), o.Normal));


  63. //计算出边缘颜色强度系数




  64. o.Emission = _RimColor.rgb * pow(rim, _RimPower)*_RimIntensity;

  65. }



  66. //——————-结束CG着色器编程语言段 || End CG Programming Part——————




  67. ENDCG

  68. }



  69. //后备着色器为普通漫反射 || Fallback use Diffuse




  70. Fallback

    “Diffuse”




  71. }



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




  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实现源代码:




  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. //———————————-【子着色器 || SubShader】—————————————




  18. SubShader

  19. {


  20. //渲染类型为Opaque,不透明 || RenderType Opaque




  21. Tags

  22. {


  23. “RenderType”


    =


    “Opaque”




  24. }



  25. //—————————————【唯一的通道 || Pass】————————————




  26. Pass

  27. {


  28. //设定通道名称 || Set Pass Name




  29. Name

    “ForwardBase”






  30. //设置光照模式 || LightMode ForwardBase




  31. Tags

  32. {


  33. “LightMode”


    =


    “ForwardBase”




  34. }



  35. //————————-开启CG着色器编程语言段 || Begin CG Programming Part———————-




  36. CGPROGRAM



  37. //【1】指定顶点和片段着色函数名称 || Set the name of vertex and fragment shader function





  38. #pragma vertex vert





  39. #pragma fragment frag






  40. //【2】头文件包含 || include





  41. #include “UnityCG.cginc”





  42. #include “AutoLight.cginc”






  43. //【3】指定Shader Model 3.0 || Set Shader Model 3.0





  44. #pragma target 3.0






  45. //【4】变量声明 || Variable Declaration





  46. //系统光照颜色




  47. uniform float4 _LightColor0;


  48. //主颜色




  49. uniform float4 _MainColor;


  50. //漫反射纹理




  51. uniform sampler2D _TextureDiffuse;


  52. //漫反射纹理_ST后缀版




  53. uniform float4 _TextureDiffuse_ST;


  54. //边缘光颜色




  55. uniform float4 _RimColor;


  56. //边缘光强度




  57. uniform

    float


    _RimPower;



  58. //边缘光强度系数




  59. uniform

    float


    _RimIntensity;




  60. //【5】顶点输入结构体 || Vertex Input Struct





  61. struct


    VertexInput


  62. {


  63. //顶点位置 || Vertex position




  64. float4 vertex : POSITION;


  65. //法线向量坐标 || Normal vector coordinates




  66. float3 normal : NORMAL;


  67. //一级纹理坐标 || Primary texture coordinates




  68. float4 texcoord : TEXCOORD0;

  69. };



  70. //【6】顶点输出结构体 || Vertex Output Struct





  71. struct


    VertexOutput


  72. {


  73. //像素位置 || Pixel position




  74. float4 pos : SV_POSITION;


  75. //一级纹理坐标 || Primary texture coordinates




  76. float4 texcoord : TEXCOORD0;


  77. //法线向量坐标 || Normal vector coordinates




  78. float3 normal : NORMAL;


  79. //世界空间中的坐标位置 || Coordinate position in world space




  80. float4 posWorld : TEXCOORD1;


  81. //创建光源坐标,用于内置的光照 || Function in AutoLight.cginc to create light coordinates




  82. LIGHTING_COORDS(3,4)

  83. };



  84. //【7】顶点着色函数 || Vertex Shader Function




  85. VertexOutput vert(VertexInput v)

  86. {


  87. //【1】声明一个顶点输出结构对象 || Declares a vertex output structure object




  88. VertexOutput o;



  89. //【2】填充此输出结构 || Fill the output structure





  90. //将输入纹理坐标赋值给输出纹理坐标




  91. o.texcoord = v.texcoord;


  92. //获取顶点在世界空间中的法线向量坐标




  93. o.normal = mul(float4(v.normal,0), _World2Object).xyz;


  94. //获得顶点在世界空间中的位置坐标




  95. o.posWorld = mul(_Object2World, v.vertex);


  96. //获取像素位置




  97. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);



  98. //【3】返回此输出结构对象  || Returns the output structure





  99. return


    o;


  100. }



  101. //【8】片段着色函数 || Fragment Shader Function




  102. fixed4 frag(VertexOutput i) : COLOR

  103. {


  104. //【8.1】方向参数准备 || Direction





  105. //视角方向




  106. float3 ViewDirection = normalize(_WorldSpaceCameraPos.xyz – i.posWorld.xyz);


  107. //法线方向




  108. float3 Normalection = normalize(i.normal);


  109. //光照方向




  110. float3 LightDirection = normalize(_WorldSpaceLightPos0.xyz);



  111. //【8.2】计算光照的衰减 || Lighting attenuation





  112. //衰减值





  113. float


    Attenuation = LIGHT_ATTENUATION(i);



  114. //衰减后颜色值




  115. float3 AttenColor = Attenuation * _LightColor0.xyz;



  116. //【8.3】计算漫反射 || Diffuse





  117. float


    NdotL = dot(Normalection, LightDirection);


  118. float3 Diffuse = max(0.0, NdotL) * AttenColor + UNITY_LIGHTMODEL_AMBIENT.xyz;



  119. //【8.4】准备自发光参数 || Emissive





  120. //计算边缘强度




  121. half Rim = 1.0 – max(0, dot(i.normal, ViewDirection));


  122. //计算出边缘自发光强度




  123. float3 Emissive = _RimColor.rgb * pow(Rim,_RimPower) *_RimIntensity;



  124. //【8.5】计在最终颜色中加入自发光颜色 || Calculate the final color





  125. //最终颜色 = (漫反射系数 x 纹理颜色 x rgb颜色)+自发光颜色 || Final Color=(Diffuse x Texture x rgbColor)+Emissive




  126. float3 finalColor = Diffuse * (tex2D(_TextureDiffuse,TRANSFORM_TEX(i.texcoord.rg, _TextureDiffuse)).rgb*_MainColor.rgb) + Emissive;



  127. //【8.6】返回最终颜色 || Return final color





  128. return


    fixed4(finalColor,1);


  129. }



  130. //——————-结束CG着色器编程语言段 || End CG Programming Part——————




  131. ENDCG

  132. }

  133. }



  134. //后备着色器为普通漫反射 || Fallback use Diffuse




  135. FallBack

    “Diffuse”




  136. }



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


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










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




















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











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









【Github】本文Shader源码