前阵子组长给我提了个需求,要实现角色人物的残影。我百度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. Sql Server 备份还原失败错误ERROR:3145(备份集中的数据库备份与现有的数据库不同)及解决办法

    SQL Server备份文件bak,备份后还原出现错误3145,备份集中的数据库备份与现有的 'xxx' 数据库不同. 解决办法如下: 1,新建一个与现有数据库重名的数据库. 如果您不知道数据库名称, ...

  2. 使用Filter跟踪Asp.net MVC页面加载时间

    最近,客户一直反馈系统使用慢,有时候能够指出具体是哪个页面,有时候又只是笼统地反馈慢.这种问题就像是幽灵一样,非常不好处理.因为导致这种问题的因素非常之多,而且在开发工程中,很难模拟出实际运行是的环境 ...

  3. db2look和db2move详解

    db2look和db2move简单实例 --- 建库create database db_name on filesystem_location using codeset utf-8 territo ...

  4. js禁止用户右键等操作

    <script type="text/javascript">    document.oncontextmenu=function(){return false};  ...

  5. 智能车学习(十二)&mdash;&mdash;智能车原理

    一.直立行走任务分解 1.任务分解 (1) 控制车模平衡:通过控制两个电机正反向运动保持车模直立平衡状态 (2) 控制车模速度:通过调节车模的倾角来实现车模速度控制,实际上最后还是演变成通过控制电机的 ...

  6. ionic下拉加载自动触发

    ionic提供的下拉加载,是要滑动去下拉加载,没有提供api自动触发下拉加载,比如刚进页面,或者切换tab时想触发一次下拉加载. 添加如下service: angular.module('YourAp ...

  7. 不要过早退出循环 while(1){no break}

    我们在尝试新的事物的时候,总是会遇到各种各样的困难,不同的人会在碰壁不同的次数之后退出.用程序员喜欢的话来说就是,我们都在for循环,区别在于你是什么情况下break的.有的人退出阈值高,这是能坚持的 ...

  8. JAVA生成RSA非对称型加密的公钥和私钥(利用JAVA API)

    非对称型加密非常适合多个客户端和服务器之间的秘密通讯,客户端使用同一个公钥将明文加密,而这个公钥不能逆向的解密,密文发送到服务器后有服务器端用私钥解密,这样就做到了明文的加密传送. 非对称型加密也有它 ...

  9. ar1020 驱动移植 无效

    移植ar1020的spi驱动.驱动移植过来后,在原来的板子上都能够正常运行,而在新的板子却没有效果. 最后查看新旧板子的AR1020的电路,发现M2引脚连接不同.M2高电平连接的touch是5线的接口 ...

  10. 如何创建Asp.net MVC ViewModel

    ASP.NET MVC View Model Patterns Since MVC has been released I have observed much confusion about how ...