C for Graphic:网格描边特效

      之后呢我会持续更新一些常用、炫酷的shader实现,从简单到复杂,从单一到复合,从我已经写过的到未来见识到的(基本上更新到不搞这一行了,上百篇应该是有的),这里来一篇最最最常用的shader。

      ——网格描边

      开发们在unity中随便创建一个cube,在Scene面板就看得到网格描边的效果,pick到这个cube后就显示出来,真的是相当实用,如图:

      C for Graphic:网格描边特效

      这里说一下这个实现原理,我们可以将这种效果分开为:

      1.中间的正常渲染的立方体

      2.中间立方体网格顶点沿着法向量方向扩展x个单位后渲染一个橘黄色的立方体(也就是描边)

      实现代码如下:

      

  1. Shader "Unlit/OutlineUnlitShader"
  2. {
  3. Properties
  4. {
  5. _CubeColor("color",color) = (1,1,1,1)
  6. _EdgeColor("edge",color) = (1,1,1,1)
  7. _EdgeThick("thick",Range(0.01,1)) = 0.1
  8. }
  9. SubShader
  10. {
  11. Tags { "RenderType"="Opaque" }
  12. LOD 100
  13. //正常渲染一个立方体的pass
  14. Pass
  15. {
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. struct appdata
  21. {
  22. float4 vertex : POSITION;
  23. };
  24. struct v2f
  25. {
  26. float4 vertex : SV_POSITION;
  27. };
  28. //渲染cube的颜色
  29. float4 _CubeColor;
  30. v2f vert (appdata v)
  31. {
  32. v2f o;
  33. //只进行最简单的MVP变换
  34. o.vertex = UnityObjectToClipPos(v.vertex);
  35. return o;
  36. }
  37. fixed4 frag (v2f i) : SV_Target
  38. {
  39. //返回单色调
  40. return _CubeColor;
  41. }
  42. ENDCG
  43. }
  44. //渲染描边的pass
  45. Pass
  46. {
  47. //这里剔除正面渲染,是因为会遮挡掉cube渲染,所以剔除掉
  48. Cull front
  49. CGPROGRAM
  50. #pragma vertex vert
  51. #pragma fragment frag
  52. #include "UnityCG.cginc"
  53. struct appdata
  54. {
  55. float4 vertex : POSITION;
  56. float4 normal : NORMAL; /*使用NORMAL语义绑定顶点法向量*/
  57. };
  58. struct v2f
  59. {
  60. float4 vertex : SV_POSITION;
  61. float4 normal : TEXCOORD1;
  62. };
  63. float4 _EdgeColor; /*描边的颜色*/
  64. float _EdgeThick; /*描边的粗细*/
  65. v2f vert (appdata v)
  66. {
  67. v2f o;
  68. //对顶点坐标进行法向量方向偏移
  69. v.vertex += v.normal * _EdgeThick;
  70. o.vertex = UnityObjectToClipPos(v.vertex);
  71. return o;
  72. }
  73. fixed4 frag (v2f i) : SV_Target
  74. {
  75. //返回描边颜色
  76. return _EdgeColor;
  77. }
  78. ENDCG
  79. }
  80. }
  81. }

  效果图如下:

  C for Graphic:网格描边特效

  给予了一个红色的外描边。

  但是实际会发现一个问题,如下的gif:

  C for Graphic:网格描边特效

        一眼就看出问题所在了,因为我们只将顶点沿着法向量方向平移,所以在平移距离过大的情况下,给人的视觉感受就是“面片分离”了,同时也能看出,unity自带的cube并非是8个顶点12个三角面,而是24个顶点12个三角面,共用的顶点全部都是分离开的,这也和引擎理解共用顶点和法向量相关,同时也涉及到渲染优化的问题,如下图:

       C for Graphic:网格描边特效

      回到正题,为了解决上面那种“面片分离”的情况,我们给每个模型顶点一个“虚拟法向量”,法向量方向为模型中心点到顶点的朝向向量,示意图如下:

      C for Graphic:网格描边特效

      接着我们实现我们的“虚拟法向量”描边代码,如下:

      

  1. //渲染描边的pass
  2. Pass
  3. {
  4. //这里剔除正面渲染,是因为会遮挡掉cube渲染,所以剔除掉
  5. Cull front
  6. CGPROGRAM
  7. #pragma vertex vert
  8. #pragma fragment frag
  9. #include "UnityCG.cginc"
  10. struct appdata
  11. {
  12. float4 vertex : POSITION;
  13. float4 normal : NORMAL; /*使用NORMAL语义绑定顶点法向量*/
  14. };
  15. struct v2f
  16. {
  17. float4 vertex : SV_POSITION;
  18. };
  19. vector _CubeCenter; /*cube的世界空间中心*/
  20. float4 _EdgeColor; /*描边的颜色*/
  21. float _EdgeThick; /*描边的粗细*/
  22. v2f vert (appdata v)
  23. {
  24. v2f o;
  25. //起算这个奇异的法向量
  26. //首先将世界空间center变换到建模空间,然后计算“虚拟”法向量
  27. float4 cube_model_center = mul(unity_WorldToObject,_CubeCenter);
  28. float3 strange_normal = normalize(v.vertex.xyz - cube_model_center.xyz);
  29. //计算法向量偏移量
  30. float4 normal_offset = float4(stangne_normal * _EdgeThick,0);
  31. v.vertex += normal_offset;
  32. o.vertex = UnityObjectToClipPos(v.vertex);
  33. return o;
  34. }
  35. fixed4 frag (v2f i) : SV_Target
  36. {
  37. //返回描边颜色
  38. return _EdgeColor;
  39. }
  40. ENDCG
  41. }

      这里我只贴上描边的pass渲染,最后的效果图如下:

      C for Graphic:网格描边特效

      是不是正常了,美中不足的就是模型的世界空间中心点需要使用脚本实时传入。

      顺便说一下,还有一种方法可以得到相同的效果,后面再来实现。

      so,我闲着无聊就继续更新着色Shader。