一些特殊的動畫,在unity中並不能使用。最近需要實現一種線條慢慢生長的過程,從無到有,還要掉頭。這種動畫貌似可以使用一些插件來使用,但想想更好的方案就是利用Shader來實現,雖然學習Shader很長時間了,但具體用它來做功能的項目還是比較少。這也算是個相對簡單的Shader,其中用到的算法也比較初級,但基本功能也初步實現了,故mark一下。
方案
1.以空物體做標記記錄關鍵點
如下圖所示,如果模型有多個轉彎的點,就可以製作多少個節點,不只侷限於cube,可以是很多彎頭的線。
2.以線爲軸心計算相對半徑
利用腳本將當前進度的點的列表傳入到Shader中,然後判斷是否滿足渲染的要求。
3.捨去不在區域內的像素
在半徑內的點渲染出來,不在半徑內的點捨去
源碼
using System;
using UnityEngine;
using System.Collections;
public class ClipLine : MonoBehaviour
{
public string matName;
public float speed = 0.2f;
public bool once = true;
public float span = 0.01f;
private Material mat;
private float lastdistence;
private float distenceAll;
private float distence;
private Vector4[] posList;
private float[] distenceList;
private int count;
void Start()
{
Renderer render = GetComponent<Renderer>();
if (string.IsNullOrEmpty(matName))
{
mat = render.material;
}
else
{
mat = Array.Find<Material>(render.materials, x => x.name == matName);
}
if (!mat || transform.childCount < 2)
{
this.enabled = false;
}
else
{
distenceList = new float[transform.childCount - 1];
for (int i = 0; i < transform.childCount; i++)
{
if (i > 0)
{
distenceList[i - 1] = Vector3.Distance(transform.GetChild(i - 1).position, transform.GetChild(i).position);
distenceAll += distenceList[i - 1];
}
}
}
}
void Update()
{
distence += Time.deltaTime * speed;
if (distence > distenceAll)
{
distence = 0;
if (once)
{
mat.SetInt("_Count", 0);
enabled = false;
return;
}
}
if (Mathf.Abs(distence - lastdistence) > 0.01f)
{
CalcutPoistionList();
OnPositionChanged();
lastdistence = distence;
}
}
void CalcutPoistionList()
{
float distenceTemp = 0f;
count = 1;
for (int i = 0; i < distenceList.Length; i++)
{
count++;
distenceTemp += distenceList[i];
if (distenceTemp > distence)
{
distenceTemp -= distenceList[i];
break;
}
}
posList = new Vector4[20];
int index = 0;
for (; index < count - 1; index++)
{
posList[index] = transform.GetChild(index).localPosition;
posList[index].w = 1;
}
posList[index] = Vector4.Lerp(transform.GetChild(index - 1).localPosition, transform.GetChild(index).localPosition,
(distence - distenceTemp) / distenceList[index - 1]);
}
void OnPositionChanged()
{
mat.SetInt("_Count", count);
#if UNITY_5_6_OR_NEWER
mat.SetVectorArray("_Pos", posList);
#elif UNITY_5_3_OR_NEWER
for (int i = 0; i < posList.Length; i++)
{
mat.SetVector("_Pos" + i, posList[i]);
}
#endif
}
}
Shader "Unlit/ClipLine"
{
Properties
{
_Color("Color",Color) = (1,1,1,1)
_MainTex("Texture", 2D) = "white" {}
_Round("Round",Float) = 0
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100
Pass
{
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
bool IsInsidePosList(float4 vec);
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _Round;
int _Count;
uniform vector _Pos[20];
int _Index;//區域
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 op :TEXCOORD1;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.op = v.vertex;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
if (!IsInsidePosList(i.op)){
clip(-1);
}
return col * _Color;
}
///在指定的區域內
bool IsInsidePosList(float4 vec)
{
if (_Count == 0) return true;
for (int i = 0; i < _Count -1; i++)
{
float3 dir1 = vec.xyz - _Pos[i + 1].xyz;
float3 dir2 = vec.xyz - _Pos[i].xyz;
float3 dirtemp = _Pos[i].xyz - _Pos[i + 1].xyz;
float dot1 = dot(dir1, dirtemp);
float dot2 = dot(dir2,-dirtemp);
if (dot1 * dot2 > 0)
{
float angle = acos(dot1 / (length(vec.xyz - _Pos[i + 1].xyz) * length(_Pos[i].xyz - _Pos[i + 1].xyz)));
float dis = length(vec.xyz - _Pos[i + 1].xyz)*sin(angle);
if (dis < _Round)
{
return true;
}
}
}
return false;
}
ENDCG
}
}
}
說明
1.距離問題
如果模型本身帶有縮放,那麼shader中的距離需要相應的調整
2.版本問題
新版本(5.6.0)的Shader格式不一樣,需要將首行重寫
3.點集問題
5.6.0傳一組數據,長度要和Shader中一樣