当前位置:   article > 正文

UnityShader基础案例(五)——物体描边_unity 描边

unity 描边

一、基于菲涅尔反射的物体描边

        1.1 基于内边缘发光的方式

                内边缘发光本身的局限性就不说了。                

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineGlowPow("描边等级",Range(0,5))=2
  8. _OutlineStrength("描边强度",Range(0,4))=1
  9. }
  10. SubShader
  11. {
  12. Pass
  13. {
  14. CGPROGRAM
  15. #pragma vertex vert
  16. #pragma fragment frag
  17. #include "UnityCG.cginc"
  18. #include "UnityLightingCommon.cginc"
  19. float4 _MainColor;
  20. fixed4 _OutlineColor;
  21. fixed _OutlineGlowPow;
  22. fixed _OutlineStrength;
  23. struct a2v
  24. {
  25. float4 vertex:POSITION;
  26. float3 normal:NORMAL;
  27. };
  28. struct v2f
  29. {
  30. float4 pos:SV_POSITION;
  31. float3 normal:TEXCOORD0;
  32. float3 worldPos:TEXCOORD1;
  33. };
  34. v2f vert(a2v v)
  35. {
  36. v2f o;
  37. o.pos = UnityObjectToClipPos(v.vertex);
  38. o.normal = UnityObjectToWorldNormal(v.normal);
  39. o.worldPos = mul(unity_ObjectToWorld, v.vertex);
  40. return o;
  41. }
  42. fixed4 frag(v2f i):SV_Target
  43. {
  44. i.normal = normalize(i.normal);
  45. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  46. fixed3 worldNormal = normalize(i.normal);
  47. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  48. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  49. (dot(worldLight, worldNormal) * 0.5 + 0.5)+ambient;
  50. float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
  51. //越边缘,值越大
  52. float fresnel = pow(1 - saturate(dot(i.normal, viewDir)), _OutlineGlowPow) * _OutlineStrength;
  53. fixed3 color = lerp(diffuse, _OutlineColor, fresnel);
  54. return fixed4(color, 1);
  55. }
  56. ENDCG
  57. }
  58. }
  59. }

                可以看到,基本上模型的每个边缘都上了色,这有区别于我们接下来说的其他描边效果。至于说整体偏红,主要是模型没任何贴图什么的毕竟白色*其他颜色的结果只会是其他颜色,而且用一张遮罩纹理也可以解决问题。              

        1.2 基于外边缘发光的方式                

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _HaloColor("外光晕颜色",Color)=(1,1,1,1)
  7. _HaloArea("外光晕范围",Range(0,2))=1
  8. _HaloPow("外光晕等级",Range(0,3))=1
  9. _HaloStrength("外光晕强度",Range(0,4))=1
  10. }
  11. SubShader
  12. {
  13. //第一个Pass实现内发光,或者说边缘发光
  14. Pass
  15. {
  16. Tags
  17. {
  18. "LightMode"="ForwardBase"
  19. }
  20. CGPROGRAM
  21. #pragma vertex vert
  22. #pragma fragment frag
  23. #include "UnityCG.cginc"
  24. #include "UnityLightingCommon.cginc"
  25. fixed4 _MainColor;
  26. struct a2v
  27. {
  28. float4 vertex:POSITION;
  29. float3 normal:NORMAL;
  30. };
  31. struct v2f
  32. {
  33. float4 pos:SV_POSITION;
  34. float3 worldNormal :COLOR;
  35. };
  36. v2f vert(a2v v)
  37. {
  38. v2f o;
  39. o.pos = UnityObjectToClipPos(v.vertex);
  40. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  41. return o;
  42. }
  43. fixed4 frag(v2f i):SV_Target
  44. {
  45. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  46. fixed3 worldNormal = normalize(i.worldNormal);
  47. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  48. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  49. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  50. return fixed4(ambient + diffuse, 1);
  51. }
  52. ENDCG
  53. }
  54. Pass
  55. {
  56. //剔除正面,防止外边缘遮住原模型,并不是所有的都需要剔除正面,比如袖子什么的
  57. //那个需要开启深度模板什么的,暂时还没学到
  58. Cull Front
  59. //混合用于控制外边缘强度
  60. Blend SrcAlpha OneMinusSrcAlpha
  61. //ZWrite off 这个用于外边缘叠加和其他物体叠加时可能会产生的透明边界的情形
  62. CGPROGRAM
  63. #pragma vertex vert
  64. #pragma fragment frag
  65. #include "UnityCG.cginc"
  66. fixed4 _HaloColor;
  67. fixed _HaloPow;
  68. fixed _HaloArea;
  69. fixed _HaloStrength;
  70. struct a2v
  71. {
  72. float4 vertex:POSITION;
  73. float3 normal:NORMAL;
  74. };
  75. struct v2f
  76. {
  77. float4 pos:SV_POSITION;
  78. float3 normal:TEXCOORD0;
  79. float4 worldPos:TEXCOORD1;
  80. };
  81. v2f vert(a2v v)
  82. {
  83. v2f o;
  84. o.normal = UnityObjectToWorldNormal(v.normal);
  85. v.vertex.xyz += v.normal * _HaloArea;
  86. o.pos = UnityObjectToClipPos(v.vertex);
  87. o.worldPos = mul(unity_ObjectToWorld, v.vertex);
  88. return o;
  89. }
  90. fixed4 frag(v2f i):SV_Target
  91. {
  92. i.normal = normalize(i.normal);
  93. //和Pass一不一样,原因是我们要把背面当正面渲染,所以视角方向要相反
  94. float3 viewDir = normalize(i.worldPos.xyz - _WorldSpaceCameraPos.xyz);
  95. //边缘仍然使用菲涅尔判断,只是和检测方式内边缘不同
  96. float fresnel = pow(saturate(dot(i.normal, viewDir)), _HaloPow) * _HaloStrength;
  97. return fixed4(_HaloColor.rgb, fresnel);
  98. }
  99. ENDCG
  100. }
  101. }
  102. }

                                       

                              

二、 基于模型扩充的描边                

        实现原理也很简单,就是模型扩充渲染一下,原模型渲染一下,结果一叠加,啪,就成了,但是要注意原模型一定要盖着扩充模型。不过说着简单,但还有很多要注意的点。

        2.1 不关闭深度写入但使用正面剔除               

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineArea("描边区域",Range(0,0.2))=2
  8. _OutlineStrength("描边强度",Range(0,1))=1
  9. }
  10. SubShader
  11. {
  12. Tags{"Queue"="Overlay"}
  13. //模型扩充渲染
  14. Pass
  15. {
  16. Cull Front
  17. //ZWrite off
  18. CGPROGRAM
  19. #pragma vertex vert
  20. #pragma fragment frag
  21. #include "UnityCG.cginc"
  22. float4 _MainColor;
  23. fixed4 _OutlineColor;
  24. fixed _OutlineArea;
  25. fixed _OutlineStrength;
  26. struct a2v
  27. {
  28. float4 vertex:POSITION;
  29. float3 normal:NORMAL;
  30. };
  31. struct v2f
  32. {
  33. float4 pos:SV_POSITION;
  34. };
  35. v2f vert(a2v v)
  36. {
  37. v2f o;
  38. v.vertex.xyz+=v.normal*_OutlineArea;
  39. o.pos = UnityObjectToClipPos(v.vertex);
  40. return o;
  41. }
  42. fixed4 frag():SV_Target
  43. {
  44. return _OutlineColor*_OutlineStrength;
  45. }
  46. ENDCG
  47. }
  48. //正常漫反射光照
  49. Pass
  50. {
  51. Tags
  52. {
  53. "LightMode"="ForwardBase"
  54. }
  55. CGPROGRAM
  56. #pragma vertex vert
  57. #pragma fragment frag
  58. #include "UnityCG.cginc"
  59. #include "UnityLightingCommon.cginc"
  60. fixed4 _MainColor;
  61. struct a2v
  62. {
  63. float4 vertex:POSITION;
  64. float3 normal:NORMAL;
  65. };
  66. struct v2f
  67. {
  68. float4 pos:SV_POSITION;
  69. float3 worldNormal :COLOR;
  70. };
  71. v2f vert(a2v v)
  72. {
  73. v2f o;
  74. o.pos = UnityObjectToClipPos(v.vertex);
  75. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  76. return o;
  77. }
  78. fixed4 frag(v2f i):SV_Target
  79. {
  80. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  81. fixed3 worldNormal = normalize(i.worldNormal);
  82. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  83. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  84. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  85. return fixed4(ambient + diffuse, 1);
  86. }
  87. ENDCG
  88. }
  89. }
  90. }

                

        2.2 关闭深度写入和剔除的方式                

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineArea("描边区域",Range(0,0.2))=2
  8. _OutlineStrength("描边强度",Range(0,1))=1
  9. }
  10. SubShader
  11. {
  12. //模型扩充渲染
  13. Pass
  14. {
  15. ZWrite off
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. float4 _MainColor;
  21. fixed4 _OutlineColor;
  22. fixed _OutlineArea;
  23. fixed _OutlineStrength;
  24. struct a2v
  25. {
  26. float4 vertex:POSITION;
  27. float3 normal:NORMAL;
  28. };
  29. struct v2f
  30. {
  31. float4 pos:SV_POSITION;
  32. };
  33. v2f vert(a2v v)
  34. {
  35. v2f o;
  36. v.vertex.xyz+=v.normal*_OutlineArea;
  37. o.pos = UnityObjectToClipPos(v.vertex);
  38. return o;
  39. }
  40. fixed4 frag():SV_Target
  41. {
  42. return _OutlineColor*_OutlineStrength;
  43. }
  44. ENDCG
  45. }
  46. Pass
  47. {
  48. Tags
  49. {
  50. "LightMode"="ForwardBase"
  51. }
  52. CGPROGRAM
  53. #pragma vertex vert
  54. #pragma fragment frag
  55. #include "UnityCG.cginc"
  56. #include "UnityLightingCommon.cginc"
  57. fixed4 _MainColor;
  58. struct a2v
  59. {
  60. float4 vertex:POSITION;
  61. float3 normal:NORMAL;
  62. };
  63. struct v2f
  64. {
  65. float4 pos:SV_POSITION;
  66. float3 worldNormal :COLOR;
  67. };
  68. v2f vert(a2v v)
  69. {
  70. v2f o;
  71. o.pos = UnityObjectToClipPos(v.vertex);
  72. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  73. return o;
  74. }
  75. fixed4 frag(v2f i):SV_Target
  76. {
  77. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  78. fixed3 worldNormal = normalize(i.worldNormal);
  79. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  80. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  81. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  82. return fixed4(ambient + diffuse, 1);
  83. }
  84. ENDCG
  85. }
  86. }
  87. }

                可以看到,只是在模型周围才有描边效果,至于说周围那条淡蓝色的细边缘,那是Unity自带的选中物体时的描边效果。

                关闭深度写入有一个问题就是描边本身会被其他物体阻挡,从而破坏描边效果。

        2.3 使用渲染队列的关闭深度写入的方式                                                           

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineArea("描边区域",Range(0,0.2))=2
  8. _OutlineStrength("描边强度",Range(0,1))=1
  9. }
  10. SubShader
  11. {
  12. Tags{"Queue"="Overlay"}
  13. //模型扩充渲染
  14. Pass
  15. {
  16. ZWrite off
  17. CGPROGRAM
  18. #pragma vertex vert
  19. #pragma fragment frag
  20. #include "UnityCG.cginc"
  21. float4 _MainColor;
  22. fixed4 _OutlineColor;
  23. fixed _OutlineArea;
  24. fixed _OutlineStrength;
  25. struct a2v
  26. {
  27. float4 vertex:POSITION;
  28. float3 normal:NORMAL;
  29. };
  30. struct v2f
  31. {
  32. float4 pos:SV_POSITION;
  33. };
  34. v2f vert(a2v v)
  35. {
  36. v2f o;
  37. v.vertex.xyz+=v.normal*_OutlineArea;
  38. o.pos = UnityObjectToClipPos(v.vertex);
  39. return o;
  40. }
  41. fixed4 frag():SV_Target
  42. {
  43. return _OutlineColor*_OutlineStrength;
  44. }
  45. ENDCG
  46. }
  47. //正常漫反射光照
  48. Pass
  49. {
  50. Tags
  51. {
  52. "LightMode"="ForwardBase"
  53. }
  54. CGPROGRAM
  55. #pragma vertex vert
  56. #pragma fragment frag
  57. #include "UnityCG.cginc"
  58. #include "UnityLightingCommon.cginc"
  59. fixed4 _MainColor;
  60. struct a2v
  61. {
  62. float4 vertex:POSITION;
  63. float3 normal:NORMAL;
  64. };
  65. struct v2f
  66. {
  67. float4 pos:SV_POSITION;
  68. float3 worldNormal :COLOR;
  69. };
  70. v2f vert(a2v v)
  71. {
  72. v2f o;
  73. o.pos = UnityObjectToClipPos(v.vertex);
  74. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  75. return o;
  76. }
  77. fixed4 frag(v2f i):SV_Target
  78. {
  79. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  80. fixed3 worldNormal = normalize(i.worldNormal);
  81. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  82. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  83. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  84. return fixed4(ambient + diffuse, 1);
  85. }
  86. ENDCG
  87. }
  88. }
  89. }

                 很明显,结果正确。        

        2.4 基于模板测试的描边效果

                也是两个Pass,第一个Pass把扩充后的模型模板值写进去,第二个Pass按原模型把更大的模板值更过即可,描边效果上面这个一样,就不演示了。

三、 一些问题

        以上是几个大写法,但这几个大写法都有些小问题,一是描边效果近大远小,一个是少顶点的描边使用扩充顶点和剔除正面都会出现的描边不贴合模型本身,         

        3.1 问题一的优化

                至于实现,大致就是将法线转换到投影空间下,这时候就能得出来,投影空间下的偏移值,在返回来加到顶点偏移上就行。       

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineArea("描边区域",Range(0,0.2))=2
  8. _OutlineStrength("描边强度",Range(0,1))=1
  9. }
  10. SubShader
  11. {
  12. Tags{"Queue"="Overlay"}
  13. //模型扩充渲染
  14. Pass
  15. {
  16. ZWrite off
  17. CGPROGRAM
  18. #pragma vertex vert
  19. #pragma fragment frag
  20. #include "UnityCG.cginc"
  21. float4 _MainColor;
  22. fixed4 _OutlineColor;
  23. fixed _OutlineArea;
  24. fixed _OutlineStrength;
  25. struct a2v
  26. {
  27. float4 vertex:POSITION;
  28. float3 normal:NORMAL;
  29. };
  30. struct v2f
  31. {
  32. float4 pos:SV_POSITION;
  33. };
  34. v2f vert(a2v v)
  35. {
  36. v2f o;
  37. float3 normal1=mul(unity_MatrixITMV,v.normal);
  38. float3 normal2=TransformViewToProjection(normal1);
  39. o.pos = UnityObjectToClipPos(v.vertex);
  40. o.pos.xy+=normal2.xy*_OutlineArea;
  41. return o;
  42. }
  43. fixed4 frag():SV_Target
  44. {
  45. return _OutlineColor*_OutlineStrength;
  46. }
  47. ENDCG
  48. }
  49. //正常漫反射光照
  50. Pass
  51. {
  52. Tags
  53. {
  54. "LightMode"="ForwardBase"
  55. }
  56. CGPROGRAM
  57. #pragma vertex vert
  58. #pragma fragment frag
  59. #include "UnityCG.cginc"
  60. #include "UnityLightingCommon.cginc"
  61. fixed4 _MainColor;
  62. struct a2v
  63. {
  64. float4 vertex:POSITION;
  65. float3 normal:NORMAL;
  66. };
  67. struct v2f
  68. {
  69. float4 pos:SV_POSITION;
  70. float3 worldNormal :COLOR;
  71. };
  72. v2f vert(a2v v)
  73. {
  74. v2f o;
  75. o.pos = UnityObjectToClipPos(v.vertex);
  76. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  77. return o;
  78. }
  79. fixed4 frag(v2f i):SV_Target
  80. {
  81. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  82. fixed3 worldNormal = normalize(i.worldNormal);
  83. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  84. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  85. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  86. return fixed4(ambient + diffuse, 1);
  87. }
  88. ENDCG
  89. }
  90. }
  91. }

                结果:效果不展示了,变化有点小,但还是有的。

        4.2 问题二:                    

                     我们上面写的全是根据法线偏移,一旦少顶点就会出现这样的结果,解决办法也很简单,沿顶点方向偏移即可。        

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. _OutlineArea("描边区域",Range(0,0.2))=2
  8. _OutlineStrength("描边强度",Range(0,1))=1
  9. }
  10. SubShader
  11. {
  12. Tags{"Queue"="Overlay"}
  13. //模型扩充渲染
  14. Pass
  15. {
  16. ZWrite off
  17. CGPROGRAM
  18. #pragma vertex vert
  19. #pragma fragment frag
  20. #include "UnityCG.cginc"
  21. float4 _MainColor;
  22. fixed4 _OutlineColor;
  23. fixed _OutlineArea;
  24. fixed _OutlineStrength;
  25. struct a2v
  26. {
  27. float4 vertex:POSITION;
  28. float3 normal:NORMAL;
  29. };
  30. struct v2f
  31. {
  32. float4 pos:SV_POSITION;
  33. };
  34. v2f vert(a2v v)
  35. {
  36. v2f o;
  37. v.vertex.xyz+=v.vertex.xyz*_OutlineArea;
  38. o.pos = UnityObjectToClipPos(v.vertex);
  39. return o;
  40. }
  41. fixed4 frag():SV_Target
  42. {
  43. return _OutlineColor*_OutlineStrength;
  44. }
  45. ENDCG
  46. }
  47. //正常漫反射光照
  48. Pass
  49. {
  50. Tags
  51. {
  52. "LightMode"="ForwardBase"
  53. }
  54. CGPROGRAM
  55. #pragma vertex vert
  56. #pragma fragment frag
  57. #include "UnityCG.cginc"
  58. #include "UnityLightingCommon.cginc"
  59. fixed4 _MainColor;
  60. struct a2v
  61. {
  62. float4 vertex:POSITION;
  63. float3 normal:NORMAL;
  64. };
  65. struct v2f
  66. {
  67. float4 pos:SV_POSITION;
  68. float3 worldNormal :COLOR;
  69. };
  70. v2f vert(a2v v)
  71. {
  72. v2f o;
  73. o.pos = UnityObjectToClipPos(v.vertex);
  74. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  75. return o;
  76. }
  77. fixed4 frag(v2f i):SV_Target
  78. {
  79. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  80. fixed3 worldNormal = normalize(i.worldNormal);
  81. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  82. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  83. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  84. return fixed4(ambient + diffuse, 1);
  85. }
  86. ENDCG
  87. }
  88. }
  89. }

       

      

四、屏幕后处理实现方式 

        后处理基于高斯模糊,而且整个过程基本是对渲染纹理的处理,第一步先渲染需要描边的物体(这里涉及到摄像机分层渲染, 然后进行高斯模糊,再和原图片做减法,留下只有模糊过的轮廓,再和整体图像做混合。

        脚本:        

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Serialization;
  6. [ExecuteInEditMode]
  7. public class RenderImage : MonoBehaviour
  8. {
  9. //用于渲染需要描边层的摄像机
  10. private GameObject cameraSecond;
  11. [Header("是否开启描边效果")] public bool isOutline = true;
  12. [Header("用于描边层提取的Shader")]public Shader shader0;
  13. [Header("实现描边的Shader")] public Shader shader1;
  14. private Camera _camera;
  15. private RenderTexture _cSRenderTexture;
  16. private Material _material;
  17. [Header("迭代次数")] [Range(0, 10)] public int iterations = 3;
  18. [Header("模糊范围")] [Range(0.2f, 20f)] public float blurArea = 0.6f;
  19. [Header("降采样")] [Range(1, 8)] private int downSample = 2;
  20. private void Awake()
  21. {
  22. cameraSecond = GameObject.Find("cameraSecond");
  23. cameraSecond.transform.SetParent(this.transform.parent);
  24. _camera = cameraSecond.GetComponent<Camera>();
  25. _material = new Material(shader1);
  26. }
  27. //在这个摄像机渲染之前,先把描边层的渲染了
  28. private void OnPreRender()
  29. {
  30. _cSRenderTexture = CustomDownSample(_cSRenderTexture);
  31. _camera.targetTexture = _cSRenderTexture;
  32. //着色器替换,使用shader0中的RenderType类型的SubShader进行渲染
  33. _camera.RenderWithShader(shader0, "RenderType");
  34. }
  35. private void OnRenderImage(RenderTexture src, RenderTexture dest)
  36. {
  37. if (_material != null&& isOutline )
  38. {
  39. int rtW = src.width / downSample;
  40. int rtH = src.height / downSample;
  41. RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH);
  42. buffer0.filterMode = FilterMode.Bilinear;
  43. Graphics.Blit(_cSRenderTexture, buffer0);
  44. //不停的模糊处理
  45. for (int i = 0; i < iterations; i++)
  46. {
  47. _material.SetFloat("_BlurArea", 1.0f + i * blurArea);
  48. RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH);
  49. //第一个Pass用于横向模糊
  50. Graphics.Blit(buffer0, buffer1, _material, 0);
  51. RenderTexture.ReleaseTemporary(buffer0);
  52. buffer0 = buffer1;
  53. buffer1 = RenderTexture.GetTemporary(rtW, rtH);
  54. //第二个Pass用于纵向模糊
  55. Graphics.Blit(buffer0, buffer1, _material, 1);
  56. RenderTexture.ReleaseTemporary(buffer0);
  57. buffer0 = buffer1;
  58. }
  59. //原图像做减法,提取出边缘
  60. //此时buffer0中存放着模糊处理后的区域
  61. _material.SetTexture("_SecondTex", buffer0);
  62. RenderTexture buffer2 = RenderTexture.GetTemporary(rtW, rtH);
  63. //第三个Pass用于获取只包含边缘的纹理
  64. Graphics.Blit(_cSRenderTexture, buffer2, _material, 2);
  65. RenderTexture.ReleaseTemporary(buffer0);
  66. buffer0 = buffer2;
  67. //最后和该摄像机进行混合
  68. _material.SetTexture("_SecondTex",buffer0);
  69. Graphics.Blit(src, dest, _material, 3);
  70. RenderTexture.ReleaseTemporary(buffer0);
  71. }
  72. else
  73. {
  74. Graphics.Blit(src, dest);
  75. }
  76. cameraSecond.SetActive(true);
  77. }
  78. private RenderTexture CustomDownSample(RenderTexture rT)
  79. {
  80. //把上一帧的申请空间先释放了
  81. RenderTexture.ReleaseTemporary(rT);
  82. rT = RenderTexture.GetTemporary(Screen.width / downSample, Screen.height / downSample);
  83. return rT;
  84. }
  85. }

              Shader:

                用于描边层提取的Shader:                

  1. Shader "Custom/Test1"
  2. {
  3. Properties
  4. {
  5. //_OutlineColor("描边颜色",Color)=(1,0,1,1)
  6. }
  7. //用于着色器替换
  8. SubShader
  9. {
  10. Tags
  11. {
  12. "RenderType"="Opaque"
  13. }
  14. Pass
  15. {
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. fixed4 _OutlineColor;
  21. struct a2v
  22. {
  23. float4 vertex:POSITION;
  24. };
  25. struct v2f
  26. {
  27. float4 pos:SV_POSITION;
  28. };
  29. v2f vert(a2v v)
  30. {
  31. v2f o;
  32. o.pos = UnityObjectToClipPos(v.vertex);
  33. return o;
  34. }
  35. fixed4 frag():SV_Target
  36. {
  37. //描边颜色在物体材质上调
  38. return _OutlineColor;
  39. }
  40. ENDCG
  41. }
  42. }
  43. }

                要描边的物体Shader:                

  1. Shader "Custom/Test2"
  2. {
  3. Properties
  4. {
  5. _MainColor("主颜色",Color)=(0,0,0,1)
  6. _OutlineColor("描边颜色",Color)=(1,1,1,1)
  7. }
  8. SubShader
  9. {
  10. Tags
  11. {
  12. "RenderType"="Opaque"
  13. }
  14. //正常漫反射光照
  15. Pass
  16. {
  17. Tags
  18. {
  19. "LightMode"="ForwardBase"
  20. }
  21. CGPROGRAM
  22. #pragma vertex vert
  23. #pragma fragment frag
  24. #include "UnityCG.cginc"
  25. #include "UnityLightingCommon.cginc"
  26. fixed4 _MainColor;
  27. struct a2v
  28. {
  29. float4 vertex:POSITION;
  30. float3 normal:NORMAL;
  31. };
  32. struct v2f
  33. {
  34. float4 pos:SV_POSITION;
  35. float3 worldNormal :COLOR;
  36. };
  37. v2f vert(a2v v)
  38. {
  39. v2f o;
  40. o.pos = UnityObjectToClipPos(v.vertex);
  41. o.worldNormal = UnityObjectToWorldNormal(v.normal);
  42. return o;
  43. }
  44. fixed4 frag(v2f i):SV_Target
  45. {
  46. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
  47. fixed3 worldNormal = normalize(i.worldNormal);
  48. fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
  49. fixed3 diffuse = _LightColor0.rgb * _MainColor.rgb *
  50. (dot(worldLight, worldNormal) * 0.5 + 0.5);
  51. return fixed4(ambient + diffuse, 1);
  52. }
  53. ENDCG
  54. }
  55. }
  56. }

        真正的重头戏:        

  1. Shader "Custom/Test0"
  2. {
  3. Properties
  4. {
  5. _MainTex("渲染纹理",2D)="white"{}
  6. _SecondTex("",2D)="white"{}
  7. _BlurArea("模糊范围",float)=1.0
  8. }
  9. SubShader
  10. {
  11. //类似C++的头文件,可以减少重复代码
  12. CGINCLUDE
  13. #include "UnityCG.cginc"
  14. sampler2D _MainTex;
  15. float4 _MainTex_TexelSize;
  16. float _BlurArea;
  17. struct a2v
  18. {
  19. float4 vertex:POSITION;
  20. float2 texcoord:TEXCOORD0;
  21. };
  22. struct v2f
  23. {
  24. float4 pos:SV_POSITION;
  25. float2 uv[5]:TEXCOORD0;
  26. };
  27. v2f vertVertical(a2v v)
  28. {
  29. v2f o;
  30. o.pos = UnityObjectToClipPos(v.vertex);
  31. //将纹理坐标计算放在顶点着色器中,由于传递是线性插值,所以并不会影响结果
  32. o.uv[0] = v.texcoord;
  33. o.uv[1] = v.texcoord + float2(0, _MainTex_TexelSize.y * 1.0) * _BlurArea;
  34. o.uv[2] = v.texcoord - float2(0, _MainTex_TexelSize.y * 1.0) * _BlurArea;
  35. o.uv[3] = v.texcoord + float2(0, _MainTex_TexelSize.y * 2.0) * _BlurArea;
  36. o.uv[4] = v.texcoord - float2(0, _MainTex_TexelSize.y * 2.0) * _BlurArea;
  37. return o;
  38. }
  39. v2f vertHorizontal(a2v v)
  40. {
  41. v2f o;
  42. o.pos = UnityObjectToClipPos(v.vertex);
  43. //注意这里是x方向的偏移
  44. o.uv[0] = v.texcoord;
  45. o.uv[1] = v.texcoord + float2(_MainTex_TexelSize.x * 1.0, 0) * _BlurArea;
  46. o.uv[2] = v.texcoord - float2(_MainTex_TexelSize.x * 1.0, 0) * _BlurArea;
  47. o.uv[3] = v.texcoord + float2(_MainTex_TexelSize.x * 2.0, 0) * _BlurArea;
  48. o.uv[4] = v.texcoord - float2(_MainTex_TexelSize.x * 2.0, 0) * _BlurArea;
  49. return o;
  50. }
  51. fixed4 frag_Blur(v2f i):SV_Target
  52. {
  53. float weight[3] = {0.4026, 0.2442, 0.0545};
  54. fixed3 color = tex2D(_MainTex, i.uv[0]) * weight[0];
  55. //权重计算
  56. for (int j = 1; j < 3; j++)
  57. {
  58. color += tex2D(_MainTex, i.uv[j * 2 - 1]) * weight[j];
  59. color += tex2D(_MainTex, i.uv[j * 2]) * weight[j];
  60. }
  61. return fixed4(color, 1);
  62. }
  63. ENDCG
  64. //用于横向模糊
  65. Pass
  66. {
  67. NAME "GAUSSIAN_VERTICAL"
  68. CGPROGRAM
  69. #pragma vertex vertHorizontal
  70. #pragma fragment frag_Blur
  71. ENDCG
  72. }
  73. //用于纵向模糊
  74. Pass
  75. {
  76. NAME "GAUSSIAN_HORIZONTAL"
  77. CGPROGRAM
  78. #pragma vertex vertVertical
  79. #pragma fragment frag_Blur
  80. ENDCG
  81. }
  82. //用于提取模糊后的边缘
  83. Pass
  84. {
  85. CGPROGRAM
  86. #pragma vertex vert
  87. #pragma fragment frag
  88. //存放着模糊后的纹理
  89. sampler2D _SecondTex;
  90. struct a2v_1
  91. {
  92. float4 vertex:POSITION;
  93. float2 texcoord:TEXCOORD0;
  94. };
  95. struct v2f_1
  96. {
  97. float4 pos:SV_POSITION;
  98. float2 uv:TEXCOORD0;
  99. };
  100. v2f_1 vert(a2v_1 v)
  101. {
  102. v2f_1 o;
  103. o.pos=UnityObjectToClipPos(v.vertex);
  104. o.uv=v.texcoord;
  105. return o;
  106. }
  107. fixed4 frag(v2f_1 i):SV_Target
  108. {
  109. fixed3 texResult1=tex2D(_MainTex,i.uv);
  110. fixed3 texResult2=tex2D(_SecondTex,i.uv);
  111. return fixed4(texResult2-texResult1,1);
  112. }
  113. ENDCG
  114. }
  115. //用于混合原图像
  116. Pass
  117. {
  118. CGPROGRAM
  119. #pragma vertex vert
  120. #pragma fragment frag
  121. //存放着提取后的纹理
  122. sampler2D _SecondTex;
  123. struct a2v_1
  124. {
  125. float4 vertex:POSITION;
  126. float2 texcoord:TEXCOORD0;
  127. };
  128. struct v2f_1
  129. {
  130. float4 pos:SV_POSITION;
  131. float2 uv:TEXCOORD0;
  132. };
  133. v2f_1 vert(a2v_1 v)
  134. {
  135. v2f_1 o;
  136. o.pos=UnityObjectToClipPos(v.vertex);
  137. o.uv=v.texcoord;
  138. return o;
  139. }
  140. fixed4 frag(v2f_1 i):SV_Target
  141. {
  142. fixed3 texResult1=tex2D(_MainTex,i.uv);
  143. fixed3 texResult2=tex2D(_SecondTex,i.uv);
  144. return fixed4(texResult2+texResult1,1);
  145. }
  146. ENDCG
  147. }
  148. }
  149. }

   

                效果很不错,而且这个描边还是有点渐变的味。         

五、基于深度纹理的实现

                UnityShader基础(八)——深度纹理与法线纹理_implosion98的博客-CSDN博客

六、还有可以基于自定义边缘风格化渲染,很离谱。                  

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/107900
推荐阅读
相关标签
  

闽ICP备14008679号