NGUI 粒子特效裁剪

方法很簡單就是給特效用的shader中傳入一個裁剪區域,然後在shader中做判斷是否在裁剪空間內,要是出了就把顏色設爲透明.
廢話不多說,直接上代碼:


Shader "AngryBots/FX/AdditiveClip" {
	Properties {
		_MainTex ("Base", 2D) = "white" {}
		_TintColor ("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)
		
		//新增 記錄裁剪框的四個邊界的值
		_Area ("Area", Vector) = (0,0,1,1)
		//----end---
	}
	
	CGINCLUDE

		#include "UnityCG.cginc"
		sampler2D _MainTex;
		fixed4 _TintColor;
		
		half4 _MainTex_ST;
		//新增,對應上面的_Area
		float4 _Area;
		//----end----
						
		struct v2f {
			half4 pos : SV_POSITION;
			half2 uv : TEXCOORD0;
			
			//新增,記錄頂點的世界座標
			float2 worldPos : TEXCOORD1;
			//----end----
		};

		v2f vert(appdata_full v) {
			v2f o;
			
			o.pos = mul (UNITY_MATRIX_MVP, v.vertex);	
			o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
			
			//新增,計算頂點的世界座標
			o.worldPos = mul(_Object2World, v.vertex).xy;
			//----end----
									
			return o; 
		}
		
		fixed4 frag( v2f i ) : COLOR {
			//新增,判斷頂點座標是否在裁剪框內
			bool inArea = i.worldPos.x >= _Area.x && i.worldPos.x <= _Area.z && i.worldPos.y >= _Area.y && i.worldPos.y <= _Area.w;
			//----end----
			return inArea ? tex2D (_MainTex, i.uv.xy) * _TintColor : fixed4(0,0,0,0);
		}
	
	ENDCG
}

說白了就是在shader中新增一個裁剪區域,因爲是用世界空間座標所以要在頂點着色器中算一下頂點的世界座標,之後就是在片源着色器中判斷一下位置,如果在裁剪區域內就正常返回,否則返回透明.

shader這邊知道怎麼寫了剩下的就是計算裁剪區域然後傳入shader中,我們用的是NGUI所以下面我說一下NGUI的做法,UGUI也同理可以看一下這篇文章,話不多說,下面看一代碼

public class ReplaceClipEffect : MonoBehaviour
{
    private UIPanel mPanel;
    private UIRoot uiRoot;
    private Transform m_Trans;
    List<Material> m_materialList = new List<Material>();//存放需要修改Shader的Material
    void Start()
    {
        m_Trans = transform;
        var trans = m_Trans;
        uiRoot = NGUITools.FindInParents<UIRoot>(gameObject);
        while (trans != null && mPanel == null)
        {
            mPanel = trans.GetComponent<UIPanel>();
            trans = trans.parent;
        }
        //獲取所有需要修改shader的material
        var particleSystems = GetComponentsInChildren<ParticleSystem>();
        for (int i = 0, j = particleSystems.Length; i < j; i++)
        {
            var ps = particleSystems[i];
            var mat = ps.GetComponent<Renderer>().material;
            m_materialList.Add(mat);
        }

        var renders = GetComponentsInChildren<MeshRenderer>();
        for (int i = 0, j = renders.Length; i < j; i++)
        {
            var ps = renders[i];
            var mat = ps.material;
            m_materialList.Add(mat);
        }

        //給shader的容器座標變量_Area賦值
        Vector4 area = CalcClipArea();
        for (int i = 0, len = m_materialList.Count; i < len; i++)
        {
            m_materialList[i].SetVector("_Area", area);
        }
    }

    private void Update()
    {
        Vector4 area = CalcClipArea();
        for (int i = 0, len = m_materialList.Count; i < len; i++)
        {
            m_materialList[i].SetVector("_Area", area);
        }
    }
    // 計算裁剪空間
    Vector4 CalcClipArea()
    {
        var clipRegion = mPanel.clipRange;
        Vector4 nguiArea = new Vector4()
        {
            x = clipRegion.x - clipRegion.z / 2,
            y = clipRegion.y - clipRegion.w / 2,
            z = clipRegion.x + clipRegion.z / 2,
            w = clipRegion.y + clipRegion.w / 2
        };

        var pos = mPanel.transform.position - uiRoot.transform.position;
        float h = 2;
        float temp = h / uiRoot.manualHeight;

        return new Vector4()
        {
            x = pos.x + nguiArea.x * temp,
            y = pos.y + nguiArea.y * temp,
            z = pos.x + nguiArea.z * temp,
            w = pos.y + nguiArea.w * temp
        };
    }
}

總的來說就是得到物體的材質球,然後計算裁剪空間挨着賦值.
說一下裁剪空間的計算,之前也是用UIPanel的clipRang計算的,但是位置不對,煩惱了好久後來發現是沒有加當前Panel到UIRoot的偏移量,說一下計算時幾個參數的作用吧
1.UIPanel的clipRange的xy存的是panel的位置,zw存的是裁剪區域的大小
2.2 / UIRoot的manualHeight計算的是UIRoot的縮放值,注意一點是UIRoot的Scaling Style要設置爲Fixed Size或FixedSizeOnMobiles,有興趣的可以看一下這篇.
3.當前panel位置減去UIroot位置算出一個偏移量(這個還沒弄懂,之後弄懂了再寫爲什麼)

這樣就可以了

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