NGUI裁剪粒子的實現,完全仿照原有的NGUI SoftClip邏輯思路,可多層裁剪

參照http://blog.csdn.net/tkokof1/article/details/52107736 這一篇博客

但是他只實現了單層panel的裁剪,雖然他的算法和NGUI的思路是一致的,但是由於傳入Shader的值不同,所以不太好移植成多層Panel裁剪。我重新對他的思路進行整理了,並且將值統一爲NGUI的規則,傳入的值都用_ClipRange0和_ClipArgs0表示。首先是C#端代碼

    void UpdateClip(UIPanel panel)
    {
        if (mCam == null) return;
        UIPanel currentPanel = panel;

        Vector4 cr = new Vector4();

        //計算距離最近的父Panel的中心點 這個點存着 如果有多層的話 就可以用panelCenter,這樣不需要重複計算了
        var panelWorldCorners1 = panel.worldCorners;
        var leftBottom1 = mCam.WorldToViewportPoint(panelWorldCorners1[0]);
        var topRight1 = mCam.WorldToViewportPoint(panelWorldCorners1[2]);
        var panelCenter = Vector3.Lerp(leftBottom1, topRight1, 0.5f);

        //遍歷所有的父Panel(所以支持嵌套的Soft Clip)
        for (int i = 0; currentPanel != null;)
        {
            //如果父Panel節點有裁剪
            if (currentPanel.hasClipping)
            {
                //這個裏面計算裁剪範圍和父Panel和當前DrawCall所屬的Panel的角度
                float angle = 0f;
                //Vector4 cr = currentPanel.drawCallClipRange;

                // 與NGUI邏輯不同的第一處 這裏將座標統一爲ViewPort座標系
                var panelWorldCorners = currentPanel.worldCorners;
                var leftBottom = mCam.WorldToViewportPoint(panelWorldCorners[0]);
                var topRight = mCam.WorldToViewportPoint(panelWorldCorners[2]);
                var center = Vector3.Lerp(leftBottom, topRight, 0.5f);
                cr.x = panelCenter.x;
                cr.y = panelCenter.y;
                cr.z = (topRight.x - leftBottom.x) / 2;
                cr.w = (topRight.y - leftBottom.y) / 2;

                //如果有多重Soft Clip的話 就會走到這裏
                // Clipping regions past the first one need additional math
                if (currentPanel != panel)
                {
                    //這裏的計算方式其實是完全一樣的 只不過計算的值統一爲ViewPort座標系了
                    Vector3 pos = panelCenter - center;
                    //Vector3 pos = currentPanel.cachedTransform.InverseTransformPoint(panel.cachedTransform.position);
                    cr.x -= pos.x;
                    cr.y -= pos.y;

                    Vector3 v0 = panel.cachedTransform.rotation.eulerAngles;
                    Vector3 v1 = currentPanel.cachedTransform.rotation.eulerAngles;
                    Vector3 diff = v1 - v0;

                    //其實這個函數就是 把角度限制在-180和180之間
                    diff.x = NGUIMath.WrapAngle(diff.x);
                    diff.y = NGUIMath.WrapAngle(diff.y);
                    diff.z = NGUIMath.WrapAngle(diff.z);

                    if (Mathf.Abs(diff.x) > 0.001f || Mathf.Abs(diff.y) > 0.001f)
                        Debug.LogWarning("Panel can only be clipped properly if X and Y rotation is left at 0", panel);

                    //因爲是界面 所以角度是平面的 只有Z是有效角度
                    angle = diff.z;
                }

                var soft = currentPanel.clipSoftness;
                var sharpness = new Vector2(1000.0f, 1000.0f);
                if (soft.x > 0f)
                {
                    sharpness.x = panel.baseClipRegion.z / soft.x;
                }
                if (soft.y > 0f)
                {
                    sharpness.y = panel.baseClipRegion.w / soft.y;
                }

                //這裏就是真正設置裁剪的地方了,我們看看這裏的各個參數的意義吧。
                //如果只是單層Soft Clip的話
                // i = 0,
                // cr就是this.panel的 drawCallClipRange,而這個drawCallClipRange的各個參數意義是這樣的: x:中心點X座標 y:中心點Y座標 z:panel width的一半, w:panel height的一半
                // currentPanel.clipSoftness 軟裁剪設置的漸變邊緣
                // angle 與父Panel的角度 如果 是單層shader的話,這個角度是0(而且shader裏面也不會用到)
                // Pass the clipping parameters to the shader
                for (int j = 0; j < _materials.Count; ++j)
                    SetClipping(_materials[j], i, cr, sharpness, angle);
                ++i;
            }
            currentPanel = currentPanel.parentPanel;
        }
    }

    void SetClipping(Material material, int index, Vector4 cr, Vector2 soft, float angle)
    {
        angle *= -Mathf.Deg2Rad;

        if (index < ClipRange.Length)
        {
            material.SetVector(ClipRange[index], new Vector4(-cr.x / cr.z, -cr.y / cr.w, 1f / cr.z, 1f / cr.w));
            material.SetVector(ClipArgs[index], new Vector4(soft.x, soft.y, Mathf.Sin(angle), Mathf.Cos(angle)));
        }
    }

具體的算法註釋上寫了 應該和NGUI的代碼90%是相似的 ,分析可以參照我的上一篇SoftClip分析:http://blog.csdn.net/nxshow/article/details/72864419


接下來是一層裁剪panel的時候用到的Shader

Shader "Custom/Particles/Additive 1"
{

	Properties{
		_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex("Particle Texture", 2D) = "white" {}
	}

	Category{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		Blend SrcAlpha One
		Cull Off Lighting Off ZWrite Off Fog{ Color(0,0,0,0) }

		SubShader{
			Pass{

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			fixed4 _TintColor;
			float4 _ClipRange0 = float4(0.0, 0.0, 1.0, 1.0);
			float2 _ClipArgs0 = float2(1000.0, 1000.0);

			struct appdata_t {
				float4 vertex : POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 vertex : SV_POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
				float2 worldPos : TEXCOORD1;
			};

			float4 _MainTex_ST;

			v2f vert(appdata_t v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.color = v.color;
				o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);

				// 統一成ViewPort座標系
				float2 clipSpace = o.vertex.xy / o.vertex.w;
				clipSpace = (clipSpace.xy + 1) * 0.5;

				o.worldPos = clipSpace * _ClipRange0.zw + _ClipRange0.xy;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed4 c = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
				// Softness factor  
				float2 factor = (float2(1, 1) - abs(i.worldPos)) * _ClipArgs0.xy;
				c.a *= clamp(min(factor.x, factor.y), 0.0, 1.0);
				return c;
			}

			ENDCG
			}
		}
	}

}

只改了一個地方,就是將座標統一成ViewPort座標系


接下來是有兩層Panel裁剪的Shader,一樣的

Shader "Custom/Particles/Additive 2"
{

	Properties{
		_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex("Particle Texture", 2D) = "white" {}
	}

	Category{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		Blend SrcAlpha One
		Cull Off Lighting Off ZWrite Off Fog{ Color(0,0,0,0) }

		SubShader{
			Pass{

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			fixed4 _TintColor;
			float4 _ClipRange0 = float4(0.0, 0.0, 1.0, 1.0);
			float4 _ClipArgs0 = float4(1000.0, 1000.0, 0.0, 1.0);
			float4 _ClipRange1 = float4(0.0, 0.0, 1.0, 1.0);
			float4 _ClipArgs1 = float4(1000.0, 1000.0, 0.0, 1.0);

			struct appdata_t {
				float4 vertex : POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 vertex : SV_POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
				float4 worldPos : TEXCOORD1;
			};

			float4 _MainTex_ST;

			float2 Rotate(float2 v, float2 rot)
			{
				float2 ret;
				ret.x = v.x * rot.y - v.y * rot.x;
				ret.y = v.x * rot.x + v.y * rot.y;
				return ret;
			}

			v2f vert(appdata_t v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.color = v.color;
				o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);

				// 統一成ViewPort座標系
				float2 clipSpace = o.vertex.xy / o.vertex.w;
				clipSpace = (clipSpace.xy + 1) * 0.5;

				o.worldPos.xy = clipSpace * _ClipRange0.zw + _ClipRange0.xy;
				o.worldPos.zw = Rotate(clipSpace, _ClipArgs1.zw) * _ClipRange1.zw + _ClipRange1.xy;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed4 c = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);

				// First clip region
				float2 factor = (float2(1.0, 1.0) - abs(i.worldPos.xy)) * _ClipArgs0.xy;
				float f = min(factor.x, factor.y);

				// Second clip region
				factor = (float2(1.0, 1.0) - abs(i.worldPos.zw)) * _ClipArgs1.xy;
				f = min(f, min(factor.x, factor.y));

				c.a *= clamp(f, 0.0, 1.0);
				return c;
			}

			ENDCG
			}
		}
	}

}


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