[Unity Shader]凌波微步效果
相信很多人都看过天龙八部,里面的段誉有一个技能就是凌波微步:移动的时候人先到,衣角跟随其后。说白了就是移动时有一个残影跟着他。下面先看下最终效果
下面我们看如何实现上面的效果。
思路:
1.既然需要移动,那么就需要一个3维(x,y,z三个方向)的数据存储,同时还需要一个变量用来表示偏移强度。
2.需要一个2d贴图来做采样
因此Shader代码很快就出来了
Shader "QShader/UnlitShader_04_1"
{
Properties
{
_MainTex ("MainTex", 2d) = "white"{}
_Direction ("Direction", vector) = (0,0,0,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _Direction;
float4 _MainTex_ST;
struct appdata
{
float4 position : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 position : SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
v.position.xyz += _Direction.xyz * _Direction.w;
o.position = UnityObjectToClipPos(v.position);
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex,i.uv);
return col;
}
ENDCG
}
}
}
注意里面的 TRANSFORM_TEX 是为了即时将变化在屏幕上显示出来。
我们先看下效果
我们创建两个材质球,第一个材质球不做任何处理,然后将第二个材质球的Direction变量的X修改为2,将两个物体做个对比观察。
我们发现物体向右边移动了。接下来我们想要的残影效果还没有,我们使用噪波算法实现随机偏移的效果。
//噪波算法
float noise = frac(sin(dot(v.uv.xy, float2(12.9898, 78.233))) * 43758.5453);
我们看到物体是整体都会被拉伸了,但是我们只需要根据他的移动方向做拉伸就好,也就是他的前进方向做拉伸,背面不做拉伸。怎么做呢?
物体在阳光下会有投影,物体的投影,也就是他的反射光线是可以根据入射光线以及他的法线来算出。这里就可以将他的不做拉伸的背面理解为他的反射光线。
那么我们就将这个反射光线作为参数传入进去
//变换拉伸
fixed NdotD = max(0,dot(_Direction,v.normal));
v2f vert (appdata v)
{
v2f o;
//噪波算法
float noise = frac(sin(dot(v.uv.xy, float2(12.9898, 78.233))) * 43758.5453);
//变换拉伸
fixed NdotD = max(0,dot(_Direction,v.normal));
v.position.xyz += _Direction.xyz * _Direction.w * noise * NdotD;
o.position = UnityObjectToClipPos(v.position);
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
return o;
}
实际就如上所示。至此完整的Shader代码已经出来了。我们增加了一个Color变量用来在贴图上面添加一个好看的颜色,这里仅是为了美观,可以去掉。
Shader "QShader/UnlitShader_04_2"
{
Properties
{
_Color ("Color",Color) = (0,0,0,1)
_MainTex ("MainTex", 2d) = "white"{}
_Direction ("Direction", vector) = (0,0,0,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _Direction;
float4 _MainTex_ST;
float4 _Color;
struct appdata
{
float4 position : POSITION;
float2 uv : TEXCOORD0;
half3 normal:NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
//噪波算法
float noise = frac(sin(dot(v.uv.xy, float2(12.9898, 78.233))) * 43758.5453);
//变换拉伸
fixed NdotD = max(0,dot(_Direction,v.normal));
v.position.xyz += _Direction.xyz * _Direction.w * noise * NdotD;
o.position = UnityObjectToClipPos(v.position);
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex,i.uv);
col+=_Color;
return col;
}
ENDCG
}
}
}
这个时候我们需要一个脚本文件来将物体移动的方向作为参数传给Shader的Direction变量,用来动态显示残影。因此新建AfterglowEffect.cs代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AfterglowEffect : MonoBehaviour {
private Material[] mats;
private Vector3 prePosition;
private Vector3 curPosition;
private float deltaTime;
// Use this for initialization
void Start()
{
prePosition = curPosition = transform.position;
Renderer[] renderers = transform.GetComponentsInChildren<Renderer>();
mats = new Material[renderers.Length];
for (int i = 0; i < renderers.Length; i++)
{
Renderer renderer = renderers[i];
mats[i] = renderer.sharedMaterial;
}
}
// Update is called once per frame
void Update()
{
curPosition = transform.position;
if (curPosition == prePosition)
{
deltaTime = 0;
return;
}
deltaTime += Time.deltaTime;
prePosition = Vector3.Lerp(prePosition,curPosition,deltaTime);
Vector3 direction = prePosition- curPosition;
for (int i = 0; i < mats.Length; i++)
{
mats[i].SetVector("_Direction", new Vector4(direction.x, direction.y, direction.z, mats[i].GetVector("_Direction").w));
}
}
}
这里有两个需要注意的地方
prePosition = Vector3.Lerp(prePosition,curPosition,deltaTime);
我们根据之前的位置和当前的位置通过Lerp函数做插值,动态传入就让残影移动的比较平滑。还有一个要注意的是我们的移动方向
Vector3 direction = prePosition- curPosition;
一般情况下移动方向是新位置减去之前的位置,但是这样会导致残影优先移动了过去,什么意思呢?就是下面这个情况
我们的移动方向是向右边,但是残影的方向其实应该是向左边,也就是反过来,这样才是对的。
参考:https://connect.unity.com/p/shaderan-li-ding-dian-yun-dong-mo-hu
欢迎关注微信公众号:Unity游戏开发笔记
QQ群: