【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
}
}