前阵子组长给我提了个需求,要实现角色人物的残影。我百度google了一下,发现可以用两种方式实现这个效果:1.记录前几帧的人物位置,将其传入shader中,对每个位置进行一个pass渲染。2. 通过相机的targetRender,记录前几帧的人物的影像,然后通过后处理混合上去。

这里先介绍方法1,先看效果:

残影用了alpha混合的方法,将它们变得透明。

先列出shader代码:

Shader "Custom/Ghost" {
    Properties {
        _MainTex ("Main Tex", 2D) = "white" {}
        _Offset0 (, , , ) // 这里只显示4个残影,所以传入4个偏移值
        _Offset1 (, , , )
        _Offset2 (, , , )
        _Offset3 (, , , )
    }

    CGINCLUDE
        #include "UnityCG.cginc"

        sampler2D _MainTex;

        float4 _Offset0;
        float4 _Offset1;
        float4 _Offset2;
        float4 _Offset3;

        struct v2f {
            float4 pos : POSITION;
            float2 uv : TEXCOORD0;
        };
    // 在shader中要渲染自身,以及4个残影,所以要定义5个不同的vert函数
        v2f vert_normal(appdata_base v) { // 渲染自身的vert函数
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.uv = v.texcoord;
            return o;
        }
    // 渲染4个残影的vert函数
        v2f vert_offset_1(appdata_base v) {
            v2f o;
            float4 pos = mul(_Object2World, v.vertex);
            o.pos = mul(UNITY_MATRIX_VP, pos + _Offset0);
            o.uv = v.texcoord;
            return o;
        }

        v2f vert_offset_2(appdata_base v) {
            v2f o;
            float4 pos = mul(_Object2World, v.vertex);
            o.pos = mul(UNITY_MATRIX_VP, pos + _Offset1);
            o.uv = v.texcoord;
            return o;
        }

        v2f vert_offset_3(appdata_base v) {
            v2f o;
            float4 pos = mul(_Object2World, v.vertex);
            o.pos = mul(UNITY_MATRIX_VP, pos + _Offset2);
            o.uv = v.texcoord;
            return o;
        }

        v2f vert_offset_4(appdata_base v) {
            v2f o;
            float4 pos = mul(_Object2World, v.vertex);
            o.pos = mul(UNITY_MATRIX_VP, pos + _Offset3);
            o.uv = v.texcoord;
            return o;
        }
             // 这里只定义了两个frag函数,分别是渲染自身,以及残影的
        float4 frag_normal(v2f i) : COLOR {
            return tex2D(_MainTex, i.uv);
        }

        float4 frag_color(v2f i) : COLOR { // 将残影的alpha值设为0.5
            float4 c;
            c = tex2D(_MainTex, i.uv);
            c.w = 0.5;
            return c;
        }
    ENDCG

    SubShader { // 这里用4个pass来渲染残影,第5个pass渲染自身
        Pass { // 从最远的开始渲染
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert_offset_4
            #pragma fragment frag_color
            ENDCG
        }

        Pass {
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert_offset_3
            #pragma fragment frag_color
            ENDCG
        }

        Pass {
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert_offset_2
            #pragma fragment frag_color
            ENDCG
        }

        Pass {
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert_offset_1
            #pragma fragment frag_color
            ENDCG
        }

        Pass { // 渲染自身,这时要开启 ZWrite
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert_normal
            #pragma fragment frag_normal
            ENDCG
        }
    }
    FallBack "Diffuse"
}

看上去挺简单的。这里要注意的一点是,从c#脚本获取的offset值,是在世界坐标中获取的,所以在计算偏移时,要先将坐标转到世界坐标。

float4 pos = mul(_Object2World, v.vertex);
o.pos = mul(UNITY_MATRIX_VP, pos + _Offset0);

接着显示C#脚本:

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

[ExecuteInEditMode]
public class Ghost : MonoBehaviour {
    public Shader curShader;
    private List<Vector3> offsets = new List<Vector3>(); // 存储前几帧的坐标
    private List<Material> mats = new List<Material>(); // 存储人物的材质,用于给shader传参数
    // Use this for initialization
    void Start ()
    {
        offsets.Add(transform.position);
        offsets.Add(transform.position);
        offsets.Add(transform.position);
        offsets.Add(transform.position);

        var skinMeshRenderer = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
        foreach (var mr in skinMeshRenderer)
            mats.Add(mr.material);

        var meshRenderer = gameObject.GetComponentsInChildren<MeshRenderer>();
        foreach (var mr in meshRenderer)
            mats.Add(mr.material);

        foreach (var mat in mats)
            mat.shader = curShader;
    }

    // Update is called once per frame
    void Update () {
        foreach (var mat in mats) // 每帧将之前的位置传入shader中
        {
            mat.SetVector(] - transform.position);
            mat.SetVector(] - transform.position);
            mat.SetVector(] - transform.position);
            mat.SetVector(] - transform.position);
        }

        offsets.Add(transform.position);
        offsets.RemoveAt();
    }
}

就这样,将C#脚本拖到GameObject身上就可以了

总结:

这个方法相对简单,问题就是,残影是和自身动作是一样的。如果要做成残影保留之前的动作,就需要记录动作参数,或者是直接保存前几帧的RenderTexture。

Unity Shader : Ghost(残影) v1的更多相关文章

  1. Unity运动残影技能

    残影实现: 1.List<DrawMesh> list,此list中包含某一帧动画模型网格.材质 2.每过一段时间就将运动物体的模型add到list中(优化:未实现,网格合并) 3.Lat ...

  2. cocos2dx - shader实现任意动画的残影效果

    本节主要讲利用cocos2dx机制实现opengl es shader脚本的绘制 这里先看下最终效果:                      这里分别实现了灰度效果及残影的效果. 一.绘制基类 这 ...

  3. Unity角色残影特效

    残影特效在网上有很多例子,比如这个,我参考着自己整合了一下,算是整合了一个比较完整且特别简单易用的出来,只需要一个脚本挂上去无需任何设定就能用. 这里只针对SkinnedMeshRenderer的网格 ...

  4. Unity3D手游开发日记(8) - 运动残影效果

    2D游戏的残影很简单,美术做序列帧图片就行了,那么3D游戏的残影美术做不了,得靠程序员动态创建模型来处理. 实现原理也很简单: 1.间隔一定时间创建一个残影模型 GameObject go = Gam ...

  5. Unity3D-Shader-人物残影效果

    [旧博客转移 - 2016年1月7日 00:24 ] 前面的话 上一篇讲了一下人物边缘发光效果,链接: Unity-ShaderLab-实现X光效果,这次我们利用这个Shader来实现人物残影效果 先 ...

  6. Unity Shader - 消融效果原理与变体

    基本原理与实现 主要使用噪声和透明度测试,从噪声图中读取某个通道的值,然后使用该值进行透明度测试. 主要代码如下: fixed cutout = tex2D(_NoiseTex, i.uvNoiseT ...

  7. Unity Shader入门

    Unity Shader入门 http://www.cnblogs.com/lixiang-share/p/5025662.html http://www.manew.com/blog-30559-1 ...

  8. Unity Shader IDE — Sublime Text2

    使用MonoDevelop写了一段时间的Shader代码,发现效率太低了,所以换用Sublime Text. 安装Sublime Text 1.下载 sublime Text2 官网:http://w ...

  9. Win7去除桌面残影的方法

    用户升级到Win7系统后使用正常,就是系统桌面会留有残影,怎么样也去不掉,影响用户的使用,那么要如何将这些残影去掉呢?可从计算机属性中进行相关配置. 解决方法 一.在计算机面板上,右键点击“计算机”, ...

随机推荐

  1. SVG描边动画原理

    SVG描边动画原理其实很简单,主要利用以下两个属性 stroke-dasharray 制作虚线,使得黑白相间, stroke-dashoffset 使得虚线向开头偏移,这里的1500不精确,是我随便取 ...

  2. 禁用backspace键的后退功能

    禁用backspace键的后退功能,但是可以删除文本内容<script language="JavaScript">document.onkeydown = check ...

  3. [Guava学习笔记]Basic Utilities: Null, 前置条件, Object方法, 排序, 异常

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3842433.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  4. [SQL SERVER系列]存储过程,游标和触发器实例[原创]

    自己写的存储过程与游标结合使用的实例,与大家分享,也供自己查阅,仅供参考: --使用游标循环处理,删除重复的记录 declare @UserID int ) ) declare @UnitFlag i ...

  5. linux 获取系统屏幕分辨率

      在Windows下可以使用GetSystemMetrics(SM_CXSCREEN);GetSystemMetrics(SM_CYSCREEN) 获取. 在Linux下可以使用XDisplayWi ...

  6. 解决:getWeatherbyCityName(city),服务器无法处理请求。 ---&gt; 未将对象引用设置到对象的实例。

    原文:getWeatherbyCityName(city),服务器无法处理请求. ---> 未将对象引用设置到对象的实例. 解决方法:不要直接使用 “服务引用” , 添加为 “Web 引用” 最 ...

  7. Javacript 学习笔记

    一.初探 javacript 学习无法是围绕着对象和属性两个方面来兜圈子,万变不离其宗. 在js中,能点出来的,或者中括号里面的必然是属性(方法).数组除外. 对象调用属性! 对象调用属性! 对象调用 ...

  8. Abp(.NetCore)开发与发布过程3-部署Ubuntu站点

    以下是笔者在 Ubuntu 16.0-64bit 环境下 发布 ABP(.NetCore)的全过程.特此记录,希望对大家有所帮助. 准备的工具 1.PuTTY(ssh,如果不想每次都用阿里云的远程登录 ...

  9. npm WARN deprecated socks@1.1.10: If using 2.x branch, please upgrade to at least 2.1.6

    cnpm安装的时候出现的一个问题. 使用npm install cnpm -g --registry=https://registry.npm.taobao.org命令的时候就会出现下图中的WARN. ...

  10. 学习lambda表达式总结

    因为最近开发涉及到大量的集合数据处理,就开始研究lambda表达式使用,看了<Java8函数式编程>,同时研究了不少博客,总结了一些基础的用法,写一篇博客,为以后的使用提供便利. 下面介绍 ...