首页 > 技术文章 > 残影效果

huang--wei 2019-05-29 13:55 原文

转载出处: 李嘉的博客 - http://www.cnblogs.com/lijiajia

 

先看一下效果:

 

大概的原理

  • 创建残影:拷贝人物当前Mesh数据作为残影,用MeshFilter+MeshRenderer渲染出来
  • 残影有生命周期、创建间隔:残影从创建到慢慢消失的过程,这里采用透明度淡出
  • 使用X光Shader
Mesh mesh = new Mesh ();
meshRender[i].BakeMesh(mesh);

 

BakeMesh拷贝网格


拷贝网格很简单,调用SkinnedMeshRenderer的BakeMesh方法

用MeshFilter跟MeshRenderer渲染网格

复制代码
MeshFilter filter = go.AddComponent<MeshFilter>();
filter.mesh = mesh;

MeshRenderer meshRen = go.AddComponent<MeshRenderer>();

meshRen.material = meshRender[i].material;
meshRen.material.shader = ghostShader;//设置xray效果
复制代码

 

MeshFilter:网格过滤器用于从你的资源中获取网格信息(Mesh)并将其传递到用于将其渲染到屏幕的网格渲染器当中

MeshRenderer:但是想要渲染出网格,还需要用到MeshRenderer哦。

完整代码

 

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

public class GhostShadow : MonoBehaviour
{
    //持续时间
    public float duration = 2f;
    //创建新残影间隔
    public float interval = 0.1f;

    //边缘颜色强度
    [Range(-1, 2)]
    public float Intension = 1;

    //网格数据
    SkinnedMeshRenderer[] meshRender;

    //X-ray
    Shader ghostShader;

    void Start()
    {
        //获取身上所有的Mesh
        meshRender = this.gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();

        ghostShader = Shader.Find("lijia/Xray");
    }

    private float lastTime = 0;

    private Vector3 lastPos = Vector3.zero;

    void Update()
    {
        //人物有位移才创建残影
        if (lastPos == this.transform.position)
        {
            return;
        }
        lastPos = this.transform.position;
        if (Time.time - lastTime < interval)
        {//残影间隔时间
            return;
        }
        lastTime = Time.time;

        if (meshRender == null)
            return;
        for (int i = 0; i < meshRender.Length; i++)
        {
            Mesh mesh = new Mesh();
            meshRender[i].BakeMesh(mesh);

            GameObject go = new GameObject();
            go.hideFlags = HideFlags.HideAndDontSave;

            GhostItem item = go.AddComponent<GhostItem>();//控制残影消失
            item.duration = duration;
            item.deleteTime = Time.time + duration;

            MeshFilter filter = go.AddComponent<MeshFilter>();
            filter.mesh = mesh;

            MeshRenderer meshRen = go.AddComponent<MeshRenderer>();

            meshRen.material = meshRender[i].material;
            meshRen.material.shader = ghostShader;//设置xray效果
            meshRen.material.SetFloat("_Intension", Intension);//颜色强度传入shader中

            go.transform.localScale = meshRender[i].transform.localScale;
            go.transform.position = meshRender[i].transform.position;
            go.transform.rotation = meshRender[i].transform.rotation;

            item.meshRenderer = meshRen;
        }
    }
}

 

 

GhostItem

 
using System;
using UnityEngine;

public class GhostItem : MonoBehaviour
{
    //持续时间
    public float duration;
    //销毁时间
    public float deleteTime;

    public MeshRenderer meshRenderer;

    void Update()
    {
        float tempTime = deleteTime - Time.time;
        if (tempTime <= 0)
        {//到时间就销毁
            GameObject.Destroy(this.gameObject);
        }
        else if (meshRenderer.material)
        {
            float rate = tempTime / duration;//计算生命周期的比例
            Color cal = meshRenderer.material.GetColor("_RimColor");
            cal.a *= rate;//设置透明通道
            meshRenderer.material.SetColor("_RimColor", cal);
        }

    }
}

 shader:

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

Shader "lijia/Xray"
{
    Properties
    {
        _RimColor("RimColor", Color) = (0, 0, 1, 1)
        _RimIntensity("Intensity", Range(0, 2)) = 1
    }
        SubShader
    {
        Tags {"Queue" = "Transparent" "RenderType" = "Opaque" }

        LOD 200
        Pass
        {
            Blend SrcAlpha One//打开混合模式
            ZWrite off
            Lighting off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal:Normal;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            fixed4 _RimColor;
            float _RimIntensity;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));//计算出顶点到相机的向量
                float val = 1 - saturate(dot(v.normal, viewDir));//计算点乘值
                o.color = _RimColor * val * (1 + _RimIntensity);//计算强度
                return o;
            }

            fixed4 frag(v2f i) : COLOR
            {
                return i.color;
            }
            ENDCG
        }
    }
}

 

缺点是比较消耗性能,频繁创建网格,相当于要多渲染那么多个人物。

 

推荐阅读