【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现

【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现


基本思路

Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘泛光Shader,以达到残影效果。

代码实现

残影类

public class ShadowFx : MonoBehaviour
{
  const string SHADOW_FX_NAME_PREFIX = "Shadow_";
  public Shader shader;//透明边缘泛光Shader
  public SkinnedMeshRenderer skinnedMeshRenderer;//蒙皮网格
  public float fadeTime = 0.5f;//淡出时间
  public MonoBehaviour coroutineMonoBehaviour;
  //协程开启对象,防止因自身关闭导致残影协程停止
  GameObject mShadowFxGO;


  void OnEnable()
  {
    coroutineMonoBehaviour.StartCoroutine(ShadowFxTrigger());//残影特效触发协程
  }

  void OnDestroy()
  {
    if (mShadowFxGO)//特殊情况残影销毁处理
      Destroy(mShadowFxGO);
  }

  IEnumerator ShadowFxTrigger()
  {
    mShadowFxGO = new GameObject(SHADOW_FX_NAME_PREFIX + Time.time);
    mShadowFxGO.transform.position = skinnedMeshRenderer.transform.position;
    mShadowFxGO.transform.rotation = skinnedMeshRenderer.transform.rotation;
    mShadowFxGO.transform.localScale = skinnedMeshRenderer.transform.localScale;
    //拷贝变换信息
    var meshRenderer = mShadowFxGO.AddComponent<MeshRenderer>();
    var meshFilter = mShadowFxGO.AddComponent<MeshFilter>();
    var mesh = new Mesh() { name = mShadowFxGO.name };
    skinnedMeshRenderer.BakeMesh(mesh);//烘焙残影
    meshFilter.sharedMesh = mesh;
    var mat = new Material(shader);
    mat.CopyPropertiesFromMaterial(skinnedMeshRenderer.sharedMaterial);
    //从主材质球拷贝参数
    meshRenderer.sharedMaterial = mat;
    var cacheMatColor = mat.color;
    var beginTime = Time.time;
    for (var duration = fadeTime; Time.time - beginTime <= duration;)
    {
      var t = (Time.time - beginTime) / duration;
      mat.color = new Color(cacheMatColor.r, cacheMatColor.g, cacheMatColor.b, Mathf.Lerp(1f, 0f, t));
      yield return null;
    }//残影消隐插值
    Destroy(mShadowFxGO);//销毁残影
  }
}

Shader

Shader "ACTBook/RimFxShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
		//一些常规字段
		_BumpMap("Normal Texture (RGB)", 2D) = "white" {}//法线贴图
		_RimColor("Rim Color", Color) = (1,0.0, 0.0, 0.0)//发光颜色
		_RimPower("Rim Power", float) = 2.0//发光强度
    }
    SubShader
    {
		Tags
		{
			"Queue" = "Transparent"
			"RenderType" = "Transparent"
		}//半透明Shader标签声明

		Pass
		{
			ZWrite On
			ColorMask 0
		}//预先写深度

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows alpha:fade
		//留意alpha:fade,使用半透明Shader须加入这一段
        #pragma target 3.0
        sampler2D _MainTex;
		fixed4 _RimColor;//边缘发光的颜色
		half _RimPower;//边缘发光的强度
        struct Input
        {
            float2 uv_MainTex;
			float2 uv_BumpMap;
			half3 viewDir;
			//边缘发光所需要的缺省参数
        };
		sampler2D _BumpMap;
        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
			half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
			//计算边缘光强度
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
			o.Emission = _RimColor.rgb * pow(rim, _RimPower);
			//乘以边缘光颜色并赋予自发光通道
            o.Alpha = rim * c.a;//边缘光强度与颜色alpha相乘,并赋予Alphat通道
        }
        ENDCG
    }
}

猜你喜欢

转载自blog.csdn.net/qq_52324195/article/details/125960688