Unity 给 Sprite Renderer 的Object 描边

参考自:https://blog.csdn.net/l773575310/article/details/78701756
代码参考自: https://github.com/ZeroChiLi/ShaderTest

从Google 搜了好多文章,大部分提供的shader 需要自己调与颜色和透明度非常的不通用。截图中的1 是网上搜的shader,不够圆滑而且需要自己调节颜色和透明度 标记2 是咱们要实现的效果。

 

步骤1,另外创建一个相机,相机的配置如下

步骤2、创建layers,在Inspector 窗口的右上角 选择layer 创建 PostEffect 

 

3、在主相机上挂载 SpriteOutline cs 文件,文件中所需要的shader和相机关联上。 Targets 就是要加添加描边的gameobject

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;


public class SpriteOutline : PostEffectsBase
{
    public Camera additionalCamera;

    public Shader drawOccupied;
    private Material occupiedMaterial = null;
    public Material OccupiedMaterial { get { return CheckShaderAndCreateMaterial(drawOccupied, ref occupiedMaterial); } }

    public Color outlineColor = Color.green;
    [Range(0, 10)]
    public int outlineWidth = 4;
    [Range(0, 9)]
    public int iterations = 1;
    [Range(0, 1)]
    public float gradient = 1;

    public GameObject[] targets;

    private MeshFilter[] meshFilters;
    private RenderTexture tempRT;

    private void Awake()
    {
        SetupAddtionalCamera();
    }

    private void SetupAddtionalCamera()
    {
        additionalCamera.CopyFrom(MainCamera);
        additionalCamera.clearFlags = CameraClearFlags.Color;
        additionalCamera.backgroundColor = Color.black;
        additionalCamera.cullingMask = 1 << LayerMask.NameToLayer("PostEffect");       // 标记渲染"PostEffect"层的物体
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (TargetMaterial != null && drawOccupied != null && additionalCamera != null && targets != null)
        {
            SetupAddtionalCamera();
            tempRT = RenderTexture.GetTemporary(source.width, source.height, 0);
            additionalCamera.targetTexture = tempRT;

            for (int i = 0; i < targets.Length; i++)
            {
                if (targets[i] == null)
                    continue;
                meshFilters = targets[i].GetComponentsInChildren<MeshFilter>();
                for (int j = 0; j < meshFilters.Length; j++)
                    if ((MainCamera.cullingMask & (1 << meshFilters[j].gameObject.layer)) != 0) // 把主相机没渲染的也不加入渲染队列
                        for (int k = 0; k < meshFilters[j].sharedMesh.subMeshCount; k++)
                            Graphics.DrawMesh(meshFilters[j].sharedMesh, meshFilters[j].transform.localToWorldMatrix, OccupiedMaterial, LayerMask.NameToLayer("PostEffect"), additionalCamera, k); // 描绘选中物体的所占面积
            }
            additionalCamera.Render();  // 需要调用渲染函数,才能及时把描绘物体渲染到纹理中

            TargetMaterial.SetTexture("_SceneTex", source);
            TargetMaterial.SetColor("_Color", outlineColor);
            TargetMaterial.SetInt("_Width", outlineWidth);
            TargetMaterial.SetInt("_Iterations", iterations);
            TargetMaterial.SetFloat("_Gradient", gradient);

            // 使用描边混合材质实现描边效果
            Graphics.Blit(tempRT, destination, TargetMaterial);

            tempRT.Release();
        }
        else
            Graphics.Blit(source, destination);
    }
}




using UnityEngine;

/// <summary>
/// 屏幕后处理效果基类
/// </summary>
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour
{
    private Camera mainCamera;
    public Camera MainCamera { get { return mainCamera = mainCamera == null ? GetComponent<Camera>() : mainCamera; } }

    public Shader targetShader;
    private Material targetMaterial = null;
    public Material TargetMaterial { get { return CheckShaderAndCreateMaterial(targetShader,ref targetMaterial); } }

    /// <summary>
    /// 检测资源,如果不支持,关闭脚本活动
    /// </summary>
    protected void Start()
    {
        if (CheckSupport() == false)
            enabled = false;
    }

    /// <summary>
    /// 检测平台是否支持图片渲染
    /// </summary>
    /// <returns></returns>
    protected bool CheckSupport()
    {
        if (SystemInfo.supportsImageEffects == false)
        {
            Debug.LogWarning("This platform does not support image effects or render textures.");
            return false;
        }
        return true;
    }

    /// <summary>
    /// 检测需要渲染的Shader可用性,然后返回使用了该shader的material
    /// </summary>
    /// <param name="shader">指定shader</param>
    /// <param name="material">创建的材质</param>
    /// <returns>得到指定shader的材质</returns>
    protected Material CheckShaderAndCreateMaterial(Shader shader, ref Material material)
    {
        if (shader == null || !shader.isSupported)
            return null;

        if (material && material.shader == shader)
            return material;

        material = new Material(shader);
        material.hideFlags = HideFlags.DontSave;
        return material;
    }
}

DrawOccupied shader

Shader "Custom/DrawOccupied"
{
    FallBack OFF
}

Outline shader

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Sprites/Outline"
{
   Properties
    {
        _MainTex("Main Texture",2D)="black"{}           // 绘制完物体面积后纹理
        _SceneTex("Scene Texture",2D)="black"{}         // 原场景纹理
        _Color("Outline Color",Color) = (0,1,0,1)       // 描边颜色
        _Width("Outline Width",int) = 4                 // 描边宽度(像素级别)
        _Iterations("Iterations",int) = 3               // 描边迭代次数(越高越平滑,消耗越高,复杂度O(n^2))
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM

            sampler2D _MainTex;
            float2 _MainTex_TexelSize;
            sampler2D _SceneTex;
            fixed4 _Color;
            float _Width;
            int _Iterations;

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (appdata_base v) 
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord.xy;  
                return o;
            }

            half4 frag(v2f i) : COLOR 
            {
                // 迭代为奇数,保证对称
                int iterations = _Iterations * 2 + 1;
                float ColorIntensityInRadius;
                float TX_x = _MainTex_TexelSize.x * _Width;
                float TX_y = _MainTex_TexelSize.y * _Width;

                // 类似高斯模糊,只是这里的核权重不重要,只要最后计算大于0,说明该像素属于外边范围内。
                for(int k = 0;k < iterations;k += 1)
                    for(int j = 0;j < iterations;j += 1)
                        ColorIntensityInRadius += tex2D(_MainTex, i.uv.xy + float2((k - iterations/2) * TX_x,(j - iterations/2) * TX_y));

                // 如果该像素有颜色(原来所占面积),或者该像素不在外边范围内,直接渲染原场景。否则就渲染为外边颜色。
                if(tex2D(_MainTex,i.uv.xy).r > 0 || ColorIntensityInRadius == 0)
                    return tex2D(_SceneTex, i.uv);
                else
                    return _Color.a * _Color + (1 - _Color.a) * tex2D(_SceneTex, i.uv);// 通过输入颜色的透明度来混合原场景。
            }
            ENDCG
        }
    }
}

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值