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