Unity Shader : Ghost(残影) v1
前阵子组长给我提了个需求,要实现角色人物的残影。我百度google了一下,发现可以用两种方式实现这个效果:1.记录前几帧的人物位置,将其传入shader中,对每个位置进行一个pass渲染。2. 通过相机的targetRender,记录前几帧的人物的影像,然后通过后处理混合上去。
这里先介绍方法1,先看效果:
残影用了alpha混合的方法,将它们变得透明。
先列出shader代码:
- Shader "Custom/Ghost" {
- Properties {
- _MainTex ("Main Tex", 2D) = "white" {}
- _Offset0 ("Offset 0", vector) = (, , , ) // 这里只显示4个残影,所以传入4个偏移值
- _Offset1 ("Offset 1", vector) = (, , , )
- _Offset2 ("Offset 2", vector) = (, , , )
- _Offset3 ("Offset 3", vector) = (, , , )
- }
- 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("_Offset0", offsets[] - transform.position);
- mat.SetVector("_Offset1", offsets[] - transform.position);
- mat.SetVector("_Offset2", offsets[] - transform.position);
- mat.SetVector("_Offset3", offsets[] - transform.position);
- }
- offsets.Add(transform.position);
- offsets.RemoveAt();
- }
- }
就这样,将C#脚本拖到GameObject身上就可以了
总结:
这个方法相对简单,问题就是,残影是和自身动作是一样的。如果要做成残影保留之前的动作,就需要记录动作参数,或者是直接保存前几帧的RenderTexture。
Unity Shader : Ghost(残影) v1的更多相关文章
- unity制作人物残影-绘制的方法
这里是利用skinnedMeshRenderer原理做的 所以脚本需要挂在带这个组件的模型上 模型shader 必须要有个_Color参数属性,并且这个值可以调节颜色,会改变人物整体的透明度 [代码下 ...
- Unity运动残影技能
残影实现: 1.List<DrawMesh> list,此list中包含某一帧动画模型网格.材质 2.每过一段时间就将运动物体的模型add到list中(优化:未实现,网格合并) 3.Lat ...
- cocos2dx - shader实现任意动画的残影效果
本节主要讲利用cocos2dx机制实现opengl es shader脚本的绘制 这里先看下最终效果: 这里分别实现了灰度效果及残影的效果. 一.绘制基类 这 ...
- Unity角色残影特效
残影特效在网上有很多例子,比如这个,我参考着自己整合了一下,算是整合了一个比较完整且特别简单易用的出来,只需要一个脚本挂上去无需任何设定就能用. 这里只针对SkinnedMeshRenderer的网格 ...
- 【Unity Shader】从NDC(归一化的设备坐标)坐标转换到世界坐标的数学原理
从NDC(归一化的设备坐标)坐标转换到世界坐标要点 参考资料 How to go from device coordinates back to worldspace http://feepingcr ...
- Unity3D手游开发日记(8) - 运动残影效果
2D游戏的残影很简单,美术做序列帧图片就行了,那么3D游戏的残影美术做不了,得靠程序员动态创建模型来处理. 实现原理也很简单: 1.间隔一定时间创建一个残影模型 GameObject go = Gam ...
- Unity3D-Shader-人物残影效果
[旧博客转移 - 2016年1月7日 00:24 ] 前面的话 上一篇讲了一下人物边缘发光效果,链接: Unity-ShaderLab-实现X光效果,这次我们利用这个Shader来实现人物残影效果 先 ...
- Unity Shader - 消融效果原理与变体
基本原理与实现 主要使用噪声和透明度测试,从噪声图中读取某个通道的值,然后使用该值进行透明度测试. 主要代码如下: fixed cutout = tex2D(_NoiseTex, i.uvNoiseT ...
- Unity Shader 玻璃效果
一个玻璃效果主要分为两个部分,一部分是折射效果的计算,另一部分则是反射.下面分类进行讨论: 折射: 1.利用Grass Pass对当前屏幕的渲染图像进行采样 2.得到法线贴图对折射的影响 3.对采集的 ...
随机推荐
- http://www.dayandeng.com/ 诈骗网站
http://www.dayandeng.com/ 诈骗网站 http://www.dayandeng.com/userfiles/media/2018/awzosv16.html 骗取你的京 ...
- Learning Puppet — Variables, Conditionals, and Facts
Begin $my_variable = "A bunch of text" notify {$my_variable:} Yup, that’s a variable, all ...
- NOR Flash擦写和原理分析 (一)
1. NOR FLASH 的简单介绍 NOR FLASH 是很常见的一种存储芯片,数据掉电不会丢失.NOR FLASH支持Execute On Chip,即程序可以直接在FLASH片内执行(这意味着存 ...
- c++常用的一些库函数、常量和头文件
1.常用数学函数 头文件 #include <math> 或者 #include <math.h> 函数原型 功能 返回值 int abs(int x) 求整数x的绝对值 ...
- 使用 Override 和 New 关键字进行版本控制
使用 Override 和 New 关键字进行版本控制 C# 语言经过专门设计,以便不同库中的基类与派生类之间的版本控制可以不断向前发展,同时保持向后兼容. 这具有多方面的意义.例如,这意味着在基类中 ...
- 单选按钮选中js的处理
function FinancialinfosetController($scope, $http, $timeout, $location, $rootScope, $routeParams) { ...
- Shuffle相关分析
Shuffle描述是一个过程,表现出的是多对多的依赖关系.Shuffle是连接map阶段和Reduce阶段的纽带,每个Reduce Task都会从Map Task产生的数据里读取其中的一片数据.Shu ...
- KMP算法的Next数组详解 转
这个写的很好,还有讲kmp,值得一看. http://www.cnblogs.com/tangzhengyue/p/4315393.html 转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法 ...
- 在tortoiseSVN上将trunk的代码merge到branch上去
1.进入branch项目的目录 2.右键选择merge 3.下一步 4.选择trunk
- Python深入01 特殊方法与多范式
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明. Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradi ...