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
			}
		}
	}

}


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