参考自: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();
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;
///
/// 屏幕后处理效果基类
///
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour
{
private Camera mainCamera;
public Camera MainCamera { get { return mainCamera = mainCamera == null ? GetComponent() : mainCamera; } }
public Shader targetShader;
private Material targetMaterial = null;
public Material TargetMaterial { get { return CheckShaderAndCreateMaterial(targetShader,ref targetMaterial); } }
///
/// 检测资源,如果不支持,关闭脚本活动
///
protected void Start()
{
if (CheckSupport() == false)
enabled = false;
}
///
/// 检测平台是否支持图片渲染
///
///
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false)
{
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
///
/// 检测需要渲染的Shader可用性,然后返回使用了该shader的material
///
/// 指定shader
/// 创建的材质
/// 得到指定shader的材质
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
}
}
}