unity描边效果实现

发表于2017-04-23
评论1 5.3k浏览

在开发中如果描边效果处理的好可以给项目加分,为此这里总结了几种在unity实现描边效果的方法,首先准备一个模型导入在unity中,使用默认shader,上传一张原始图,以便后面实现功能效果的对比:



一、边缘光,这里参照官方的一个SurfaceShader Example,Rim Lighting

1.在unity创建一个SurfaceShader,命名RimLighting

  1. Shader "Custom/RimLighting" {  
  2.     Properties {  
  3.         _Color ("Color", Color) = (1,1,1,1)  
  4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}  
  5.         //边缘光颜色  
  6.         _RimColor("Rim Color",Color) =(1,1,1,1)  
  7.         //边缘光强度  
  8.         _RimPower("Rim Power", Range(0.5,8.0)) = 3.0  
  9.     }  
  10.     SubShader {  
  11.         Tags { "RenderType"="Opaque" }  
  12.         LOD 200  
  13.           
  14.         CGPROGRAM  
  15.         // Physically based Standard lighting model, and enable shadows on all light types  
  16.         #pragma surface surf Standard fullforwardshadows  
  17.   
  18.         // Use shader model 3.0 target, to get nicer looking lighting  
  19.         #pragma target 3.0  
  20.   
  21.         sampler2D _MainTex;  
  22.   
  23.         struct Input {  
  24.             float2 uv_MainTex;  
  25.             //法线  
  26.             float3 worldNormal;  
  27.             //视角方向  
  28.             float3 viewDir;  
  29.         };  
  30.   
  31.         fixed4 _Color;  
  32.         fixed4 _RimColor;  
  33.         half _RimPower;  
  34.   
  35.         void surf (Input IN, inout SurfaceOutputStandard o) {  
  36.             // Albedo comes from a texture tinted by color  
  37.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;  
  38.             o.Albedo = c.rgb;  
  39.             o.Alpha = c.a;  
  40.               
  41.             half rim = 1.0 - saturate(dot(normalize(IN.viewDir), IN.worldNormal));  
  42.             o.Emission = _RimColor.rgb * pow(rim, _RimPower);  
  43.         }  
  44.         ENDCG  
  45.     }  
  46.     FallBack "Diffuse"  
  47. }  

2.将模型材质的shader改为刚才所写的shader,Custom/RimLighting


3.更改后,具体效果如下



二、法线外拓,用一个Pass渲染边框,一个Pass渲染实物

  1. 创建一个UnlitShader,命名为NormalUnlitShader

  1. Shader "Custom/NormalUnlitShader"  
  2. {  
  3.     Properties  
  4.     {  
  5.         _MainTex ("Texture", 2D) = "white" {}  
  6.         _Outline("Out Line",Range(0.001,0.005))=0.002  
  7.         _Color("Color",Color)=(1,1,1,1)  
  8.     }  
  9.   
  10.     CGINCLUDE  
  11.     #include "UnityCG.cginc"  
  12.     struct v2f  
  13.     {  
  14.         float4 pos:POSITION;  
  15.         float4 color:COLOR;  
  16.     };  
  17.   
  18.     sampler2D _MainTex;  
  19.     float _Outline;  
  20.     fixed4 _Color;  
  21.   
  22.     v2f vert(appdata_base v)  
  23.     {  
  24.         v2f o;  
  25.         o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
  26.         float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);  
  27.         float2 offset = TransformViewToProjection(norm.xy);  
  28.         o.pos.xy += offset * o.pos.z * _Outline;  
  29.         o.color = _Color;  
  30.         return o;  
  31.     }  
  32.     ENDCG  
  33.   
  34.     SubShader  
  35.     {  
  36.         Cull Front  
  37.         Pass  
  38.         {  
  39.             CGPROGRAM  
  40.             #pragma vertex vert  
  41.             #pragma fragment frag  
  42.             fixed4 frag (v2f i) : COLOR  
  43.             {  
  44.                 return i.color;  
  45.             }  
  46.             ENDCG  
  47.         }  
  48.   
  49.         CGPROGRAM  
  50.         #pragma surface surf Lambert    
  51.         struct Input {  
  52.             float2 uv_MainTex;  
  53.         };  
  54.         void surf(Input IN, inout SurfaceOutput o) {  
  55.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex);  
  56.             o.Albedo = c.rgb;  
  57.             o.Alpha = c.a;  
  58.         }  
  59.         ENDCG  
  60.     }  
  61. }  

2.这里再换成新建的NormalUnlitShader,就会发现一些问题,他会在一些我们并不像描边的地方也改变了颜色,这就是因为这根据模型法线并不是全部都均匀的向外扩展,才导致这样的情况



3.再换一个Sphere模型应用与怪物同一个材质球,就会发现Sphere模型,是能达到我们的需求的,也能很明显的看出两者的差别,因为球的法线都是均匀的往外扩展的,这个方法的使用就需要以后根据实际的要求来使用



三、屏幕特效,描边效果

  1. 新建一个辅助摄像机,设置参数如下,并将模型的Layer设置为Monster,这样辅助摄像机就能单独看见这个怪物模型


2.写一个纯色shader,辅助摄像机用RenderWithShader,纯色渲染一张纹理处理

  1. Shader "Custom/UnlitSolidColor"  
  2. {  
  3.     SubShader  
  4.     {  
  5.         Pass  
  6.         {  
  7.             //返回蓝色  
  8.             Color(0,0,1,1)  
  9.         }  
  10.     }  
  11. }  

3.将纯色纹理,可以模糊放大几次,次数越多,边框就越宽,这里需要使用到一个像素偏移函数,Graphics.BlitMultiTap和一个Blur效果shader

  1. Shader "Custom/OutterLineBlur" {  
  2.   
  3. Properties {  
  4.     _MainTex ("", 2D) = "white" {}  
  5. }  
  6.   
  7. Category {  
  8.     ZTest Always Cull Off ZWrite Off Fog { Mode Off }  
  9.   
  10.     Subshader {  
  11.         Pass {  
  12.             CGPROGRAM  
  13.                 #pragma vertex vert  
  14.                 #pragma fragment frag  
  15.                 #pragma fragmentoption ARB_precision_hint_fastest  
  16.  
  17.                 #include "UnityCG.cginc"  
  18.   
  19.                 struct v2f {  
  20.                     float4 pos : POSITION;  
  21.                     half4 uv[2] : TEXCOORD0;  
  22.                 };  
  23.   
  24.                 float4 _MainTex_TexelSize;  
  25.                 float4 _BlurOffsets;  
  26.   
  27.                 v2f vert (appdata_img v)  
  28.                 {  
  29.                     v2f o;  
  30.                     float offX = _MainTex_TexelSize.x * _BlurOffsets.x;  
  31.                     float offY = _MainTex_TexelSize.y * _BlurOffsets.y;  
  32.   
  33.                     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);  
  34.                     float2 uv = MultiplyUV (UNITY_MATRIX_TEXTURE0, v.texcoord.xy-float2(offX, offY));  
  35.                   
  36.                     o.uv[0].xy = uv + float2( offX, offY);  
  37.                     o.uv[0].zw = uv + float2(-offX, offY);  
  38.                     o.uv[1].xy = uv + float2( offX,-offY);  
  39.                     o.uv[1].zw = uv + float2(-offX,-offY);  
  40.                     return o;  
  41.                 }  
  42.                   
  43.                 sampler2D _MainTex;  
  44.                 fixed4 _Color;  
  45.   
  46.                 fixed4 frag( v2f i ) : COLOR  
  47.                 {  
  48.                     fixed4 c;  
  49.                     c  = tex2D( _MainTex, i.uv[0].xy );  
  50.                     c += tex2D( _MainTex, i.uv[0].zw );  
  51.                     c += tex2D( _MainTex, i.uv[1].xy );  
  52.                     c += tex2D( _MainTex, i.uv[1].zw );  
  53.                     return c /2 ;  
  54.                 }  
  55.                 ENDCG  
  56.             }  
  57.         }  
  58.     }  
  59.     Fallback off  
  60. }  

4.将扩大后的纹理与原来的纹理,做一个对比,并依据原来纹理剔除掉中间部分,就只剩下一个边框纹理,这里需要使用一个剔除shader

  1. Shader "Custom/OutterLineCutoff" {  
  2.     Properties {  
  3.         _MainTex ("", 2D) = "white" {}  
  4.     }     
  5.     Category {  
  6.     BlendOp RevSub  
  7.     Blend One One  
  8.     ZTest Always Cull Off ZWrite Off Fog { Mode Off }  
  9.       
  10.     Subshader {  
  11.         Pass {  
  12.             CGPROGRAM  
  13.             #pragma vertex vert  
  14.             #pragma fragment frag  
  15.             #pragma fragmentoption ARB_precision_hint_fastest   
  16.               
  17.             sampler2D _MainTex;  
  18.             sampler2D _MainTex1;  
  19.             struct appdata  
  20.             {  
  21.                 float4 vertex : POSITION;  
  22.                 float4 texcoord : TEXCOORD0;  
  23.             };  
  24.             struct v2f  
  25.             {  
  26.                 float4 pos : SV_POSITION;  
  27.                 float2 uv : TEXCOORD0;  
  28.             };  
  29.             v2f vert (appdata v)  
  30.             {  
  31.                 v2f o;  
  32.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
  33.                 o.uv = v.texcoord.xy;  
  34.                 return o;  
  35.             }  
  36.             half4 frag(v2f i) : COLOR  
  37.             {  
  38.                 fixed4 c =  tex2D(_MainTex, i.uv);  
  39.                 return c;  
  40.             }  
  41.             ENDCG  
  42.         }  
  43.         }     
  44.     }  
  45.     FallBack "Diffuse"  
  46. }  

5.在主摄像机上,使用OnRenderImage函数,将得到的轮廓纯色纹理与摄像机的图像使用混合shader进行混合

  1. Shader "Custom/OutterLineComposer" {  
  2.   
  3. Properties {  
  4.     _MainTex ("", 2D) = "white" {}  
  5. }  
  6.   
  7. Category {  
  8.     ZTest Always Cull Off ZWrite Off Fog { Mode Off }  
  9.     Blend SrcAlpha OneMinusSrcAlpha  
  10.   
  11.     Subshader {  
  12.         Pass {  
  13.             CGPROGRAM  
  14.                 #pragma vertex vert  
  15.                 #pragma fragment frag  
  16.                 #pragma fragmentoption ARB_precision_hint_fastest  
  17.  
  18.                 #include "UnityCG.cginc"  
  19.   
  20.                 struct v2f {  
  21.                     float4 pos : POSITION;  
  22.                     half2 uv : TEXCOORD0;  
  23.                 };  
  24.   
  25.                 v2f vert (appdata_img v)  
  26.                 {  
  27.                     v2f o;  
  28.                     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);  
  29.                     o.uv = v.texcoord.xy;  
  30.                     return o;  
  31.                 }  
  32.   
  33.                 sampler2D _MainTex;  
  34.   
  35.                 fixed4 frag( v2f i ) : COLOR  
  36.                 {  
  37.                     return tex2D( _MainTex, i.uv );  
  38.                 }  
  39.                 ENDCG  
  40.             }  
  41.         }  
  42.     }  
  43.     Fallback off  
  44. }  

6.绑定在主摄像机的脚本

  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. [ExecuteInEditMode]  
  5. public class outline : MonoBehaviour {  
  6.     ///   
  7.     /// 辅助摄像机  
  8.     ///   
  9.     public Camera outlineCamera;  
  10.   
  11.     #region 纯红色材质 solidColorMaterail  
  12.     public Shader solidColorShader;  
  13.     private Material m_solid=null;  
  14.     private Material solidMaterail  
  15.     {  
  16.         get   
  17.         {  
  18.             if (m_solid == null)  
  19.             {  
  20.                 m_solid = new Material(solidColorShader);  
  21.             }  
  22.             return m_solid;  
  23.         }  
  24.     }  
  25.     #endregion  
  26.      
  27.     #region 合并材质 compositeMaterial  
  28.     public Shader compositeShader;  
  29.     private Material m_composite=null;  
  30.     private Material compositeMaterial  
  31.     {  
  32.         get  
  33.         {  
  34.             if (m_composite == null)  
  35.                 m_composite = new Material(compositeShader);  
  36.             return m_composite;  
  37.         }  
  38.     }  
  39.     #endregion  
  40.       
  41.     #region 模糊材质 blurMaterial  
  42.     public Shader blurShader;  
  43.     private Material m_blur=null;  
  44.     private Material blurMaterial  
  45.     {  
  46.         get   
  47.         {  
  48.             if (m_blur == null)  
  49.                 m_blur = new Material(blurShader);  
  50.             return m_blur;  
  51.         }  
  52.     }  
  53.     #endregion  
  54.       
  55.     #region 剔除材质 cutoffShader  
  56.     public Shader cutoffShader;  
  57.     private Material m_cutoff=null;  
  58.     private Material cutoffMaterial  
  59.     {  
  60.         get  
  61.         {  
  62.             if (m_cutoff == null)  
  63.                 m_cutoff = new Material(cutoffShader);  
  64.             return m_cutoff;  
  65.         }  
  66.     }  
  67.     #endregion  
  68.     ///   
  69.     /// 辅助摄像机渲染的RenderTexture  
  70.     ///   
  71.     private RenderTexture outlineRenderTex;  
  72.     ///   
  73.     /// 模糊扩大次数  
  74.     ///   
  75.     public int Iterations = 2;  
  76.     // Use this for initialization  
  77.     void Start () {  
  78.         outlineRenderTex = new RenderTexture((int)outlineCamera.pixelWidth, (int)outlineCamera.pixelHeight, 16);  
  79.     }  
  80.   
  81.     // Update is called once per frame  
  82.     void Update () {  
  83.     }  
  84.   
  85.     void OnPreRender()  
  86.     {  
  87.         outlineCamera.targetTexture = outlineRenderTex;  
  88.         outlineCamera.RenderWithShader(solidMaterail.shader, "");  
  89.     }  
  90.   
  91.     void OnRenderImage(RenderTexture source, RenderTexture desture)  
  92.     {  
  93.         RenderTexture _renderTexture = RenderTexture.GetTemporary(outlineRenderTex.width, outlineRenderTex.height, 0);  
  94.   
  95.         MixRender(outlineRenderTex,ref _renderTexture);  
  96.          
  97.         Graphics.Blit(_renderTexture, desture, compositeMaterial);  
  98.         RenderTexture.ReleaseTemporary(_renderTexture);  
  99.     }  
  100.   
  101.       
  102.     void MixRender(RenderTexture in_outerTexture, ref RenderTexture _renderTexture)  
  103.     {  
  104.         RenderTexture buffer = RenderTexture.GetTemporary(in_outerTexture.width, in_outerTexture.height, 0);  
  105.         RenderTexture buffer2 = RenderTexture.GetTemporary(in_outerTexture.width, in_outerTexture.height, 0);  
  106.   
  107.         Graphics.Blit(in_outerTexture, buffer);  
  108.   
  109.         //多次模糊放大  
  110.         for (int i = 0; i < Iterations; i++)  
  111.         {  
  112.             FourTapCone(buffer, buffer2, i);  
  113.             Graphics.Blit(buffer2, buffer);  
  114.         }  
  115.         Graphics.Blit(in_outerTexture, buffer, cutoffMaterial);  
  116.         Graphics.Blit(buffer, _renderTexture);  
  117.         
  118.         RenderTexture.ReleaseTemporary(buffer);  
  119.         RenderTexture.ReleaseTemporary(buffer2);  
  120.     }  
  121.   
  122.     float Spread = 0.8f;  
  123.     public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration)  
  124.     {  
  125.         float off = 0.5f + iteration * Spread;  
  126.         Graphics.BlitMultiTap(source, dest, blurMaterial,  
  127.                                new Vector2(off, off),  
  128.                                new Vector2(-off, off),  
  129.                                new Vector2(off, -off),  
  130.                                new Vector2(-off, -off)  
  131.                                );  
  132.     }  
  133.   
  134. }  

7.具体效果如下,因为这里是在主摄像机设置的屏幕特效,他可以忽略掉所有的遮挡,这是优点也是弊端

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引