模型淡出Shader實現

一些特殊的動畫,在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中一樣






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章