Unity后处理实现物体外描边

来源:互联网 发布:淘宝买书有什么好店铺 编辑:程序博客网 时间:2024/06/06 01:54

效果

这里写图片描述

原理

使用后处理,

  1. 在后处理阶段先渲染产生一张RenderTexture,包含要被描边的物体,使用描边色渲染。
  2. 高斯模糊RenderTexture,会产生边缘
  3. 用高斯模糊的图片反向剔除未模糊的图,这样只保留模糊多出的部分。
  4. 此时RenderTexture上为只有边缘的图,将此边缘图与渲染结果图进行混合并输出屏幕,即得到结果。

实现

OutLineCameraComponent.cs 挂载在摄像机下,赋值两个shader。

using UnityEngine;  using System.Collections.Generic;using UnityEngine.Rendering;  [ExecuteInEditMode]public class OutLineCameraComponent{      private RenderTexture renderTexture = null;      private CommandBuffer commandBuffer = null;      [Header("Outline/OutLineEffect.shader")]    public Shader preoutlineShader = null;     [Header("Outline/OutlinePrePass.shader")]     public Shader shader = null;    private Material _material = null;    public Material mMaterial    {        get        {            if (_material == null)                _material = GenerateMaterial(shader);            return _material;        }    }    [Header("采样范围")]    public float samplerArea = 1;      [Header("降分辨率")]    public int downSample = 1;      [Header("迭代次数")]    public int iteration = 2;    [Header("描边强度")]    [Range(0.0f, 10.0f)]      public float outLineStrength = 3.0f;      //根据shader创建用于屏幕特效的材质        protected Material GenerateMaterial(Shader shader)        {            if (shader == null)                return null;            if (shader.isSupported == false)                return null;            Material material = new Material(shader);            material.hideFlags = HideFlags.DontSave;            if (material)                return material;            return null;        }    //目标对象      private List<OutLineTargetComponent> targetObjects = new List<OutLineTargetComponent>();    public void AddTarget(OutLineTargetComponent target)    {        if(target.material==null)            target.material = new Material(preoutlineShader);        targetObjects.Add(target);        RefreshCommandBuff();    }    public void RemoveTarget(OutLineTargetComponent target)    {        bool found = false;        for (int i = 0; i < targetObjects.Count; i++)        {            if (targetObjects[i] == target)            {                targetObjects.Remove(target);                DestroyImmediate(target.material);                target.material = null;                found = true;                break;            }        }        if(found)            RefreshCommandBuff();    }    public void RefreshCommandBuff()    {        if (renderTexture)              RenderTexture.ReleaseTemporary(renderTexture);          renderTexture = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0);          commandBuffer = new CommandBuffer();          commandBuffer.SetRenderTarget(renderTexture);          commandBuffer.ClearRenderTarget(true, true, Color.black);        for (int i = 0; i < targetObjects.Count; i++)        {            Renderer[] renderers = targetObjects[i].GetComponentsInChildren<Renderer>();              foreach (Renderer r in renderers)                commandBuffer.DrawRenderer(r, targetObjects[i].material);        }    }   void OnEnable()      {          if (preoutlineShader == null)              return;          RefreshCommandBuff();    }      void OnDisable()      {          if (renderTexture)          {              RenderTexture.ReleaseTemporary(renderTexture);              renderTexture = null;          }          if (commandBuffer != null)          {              commandBuffer.Release();              commandBuffer = null;          }      }      void OnRenderImage(RenderTexture source, RenderTexture destination)      {          if (mMaterial && renderTexture && commandBuffer != null)          {              Graphics.ExecuteCommandBuffer(commandBuffer);              //对RT进行Blur处理              RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);              RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);              //高斯模糊,两次模糊,横向纵向,使用pass0进行高斯模糊              mMaterial.SetVector("_offsets", new Vector4(0, samplerArea, 0, 0));              Graphics.Blit(renderTexture, temp1, mMaterial, 0);              mMaterial.SetVector("_offsets", new Vector4(samplerArea, 0, 0, 0));              Graphics.Blit(temp1, temp2, mMaterial, 0);              //如果有叠加再进行迭代模糊处理              for(int i = 0; i < iteration; i++)              {                  mMaterial.SetVector("_offsets", new Vector4(0, samplerArea, 0, 0));                  Graphics.Blit(temp2, temp1, mMaterial, 0);                  mMaterial.SetVector("_offsets", new Vector4(samplerArea, 0, 0, 0));                  Graphics.Blit(temp1, temp2, mMaterial, 0);              }              //用模糊图和原始图计算出轮廓图            mMaterial.SetTexture("_BlurTex", temp2);              Graphics.Blit(renderTexture, temp1, mMaterial, 1);              //轮廓图和场景图叠加              mMaterial.SetTexture("_BlurTex", temp1);              mMaterial.SetFloat("_OutlineStrength", outLineStrength);              Graphics.Blit(source, destination, mMaterial, 2);              RenderTexture.ReleaseTemporary(temp1);              RenderTexture.ReleaseTemporary(temp2);          }          else          {              Graphics.Blit(source, destination);          }      }  }  

OutLineTargetComponent.cs 挂载在要使用描边的物体上

using UnityEngine;[ExecuteInEditMode]public class OutLineTargetComponent : MonoBehaviour{    public Color color = Color.green;    public Material material { set; get; }    void OnEnable()    {        Camera[] allCameras = Camera.allCameras;        for (int i = 0; i < allCameras.Length; i++)        {            if (allCameras[i].GetComponent<OutLineCameraComponent>()!=null)            {                allCameras[i].GetComponent<OutLineCameraComponent>().AddTarget(this);            }        }    }    private void Update()    {        if(material!=null)            material.SetColor("_OutLineColor", color);      }    void OnDisable()    {        Camera[] allCameras = Camera.allCameras;        for (int i = 0; i < allCameras.Length; i++)        {            if (allCameras[i].GetComponent<OutLineCameraComponent>()!=null)            {                allCameras[i].GetComponent<OutLineCameraComponent>().RemoveTarget(this);            }        }    }}

OutlinePrePass.shader

Shader "OutLine/OutlinePrePass"  {    SubShader      {        Pass          {            CGPROGRAM            #include "UnityCG.cginc"              fixed4 _OutLineColor;            struct v2f              {                  float4 pos : SV_POSITION;              };              v2f vert(appdata_full v)              {                  v2f o;                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);                  return o;              }            fixed4 frag(v2f i) : SV_Target              {                  return _OutLineColor;            }              #pragma vertex vert              #pragma fragment frag              ENDCG          }      }  }  

OutLineEffect.shader

Shader "OutLine/OutLineEffect" {      Properties{          _MainTex("Base (RGB)", 2D) = "white" {}          _BlurTex("Blur", 2D) = "white"{}      }      CGINCLUDE      #include "UnityCG.cginc"      //用于剔除中心留下轮廓      struct v2f_cull      {          float4 pos : SV_POSITION;          float2 uv : TEXCOORD0;      };      //用于模糊      struct v2f_blur      {          float4 pos : SV_POSITION;          float2 uv  : TEXCOORD0;          float4 uv01 : TEXCOORD1;          float4 uv23 : TEXCOORD2;          float4 uv45 : TEXCOORD3;      };      //用于最后叠加      struct v2f_add      {          float4 pos : SV_POSITION;          float2 uv  : TEXCOORD0;          float2 uv1 : TEXCOORD1;      };      sampler2D _MainTex;      float4 _MainTex_TexelSize;      sampler2D _BlurTex;      float4 _BlurTex_TexelSize;      float4 _offsets;      float _OutlineStrength;      //获得轮廓 pass 1    v2f_cull vert_cull(appdata_img v)      {          v2f_cull o;          o.pos = mul(UNITY_MATRIX_MVP, v.vertex);          o.uv = v.texcoord.xy;          //dx中纹理从左上角为初始坐标,需要反向  #if UNITY_UV_STARTS_AT_TOP          if (_MainTex_TexelSize.y < 0)              o.uv.y = 1 - o.uv.y;  #endif            return o;      }      fixed4 frag_cull(v2f_cull i) : SV_Target      {          fixed4 colorMain = tex2D(_MainTex, i.uv);          fixed4 colorBlur = tex2D(_BlurTex, i.uv);          return colorBlur - colorMain;      }      //高斯模糊 pass 0    v2f_blur vert_blur(appdata_img v)      {          v2f_blur o;          _offsets *= _MainTex_TexelSize.xyxy;          o.pos = mul(UNITY_MATRIX_MVP, v.vertex);          o.uv = v.texcoord.xy;          o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);          o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;          o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;          return o;      }      fixed4 frag_blur(v2f_blur i) : SV_Target      {          fixed4 color = fixed4(0,0,0,0);          color += 0.40 * tex2D(_MainTex, i.uv);          color += 0.15 * tex2D(_MainTex, i.uv01.xy);          color += 0.15 * tex2D(_MainTex, i.uv01.zw);          color += 0.10 * tex2D(_MainTex, i.uv23.xy);          color += 0.10 * tex2D(_MainTex, i.uv23.zw);          color += 0.05 * tex2D(_MainTex, i.uv45.xy);          color += 0.05 * tex2D(_MainTex, i.uv45.zw);          return color;      }      //最终叠加 pass 2    v2f_add vert_final(appdata_img v)      {          v2f_add o;          o.pos = mul(UNITY_MATRIX_MVP, v.vertex);          o.uv.xy = v.texcoord.xy;          o.uv1.xy = o.uv.xy;  #if UNITY_UV_STARTS_AT_TOP          if (_MainTex_TexelSize.y < 0)              o.uv.y = 1 - o.uv.y;  #endif            return o;      }      fixed4 frag_final(v2f_add i) : SV_Target      {          fixed4 ori = tex2D(_MainTex, i.uv1);          fixed4 blur = tex2D(_BlurTex, i.uv);          fixed4 final = ori + blur * _OutlineStrength;          return final;      }          ENDCG      SubShader      {          //pass 0: 高斯模糊          Pass          {              ZTest Off              Cull Off              ZWrite Off              Fog{ Mode Off }              CGPROGRAM              #pragma vertex vert_blur              #pragma fragment frag_blur              ENDCG          }          //pass 1: 剔除中心部分           Pass          {              ZTest Off              Cull Off              ZWrite Off              Fog{ Mode Off }              CGPROGRAM              #pragma vertex vert_cull            #pragma fragment frag_cull            ENDCG          }          //pass 2: 最终叠加          Pass          {              ZTest Off              Cull Off              ZWrite Off              Fog{ Mode Off }              CGPROGRAM              #pragma vertex vert_final            #pragma fragment frag_final              ENDCG          }      }  }
原创粉丝点击