[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羣: