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