新手引导

挖洞、点击穿透、毛玻璃

Posted by Bob on March 8, 2019

新手引导需要处理的几种效果

shader处理挖洞

image

挖矩形洞 image

挖圆形洞 image


Shader "Unlit/MaskShader"
{
	Properties
	{
		
		_CropColor("CropColor",Color) = (0,0,0,0)
		_Fade("Fade",float) = 2

		//crop circle (centerx,centery,radius,0)
		//crop rectangle (centerx,centery,width,height)
		_Crop("Crop(worldx,worldy,width,height)",Vector) = (640,320,100,100)
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100
		Blend SrcAlpha OneMinusSrcAlpha

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float4 color:Color;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 color:Color;
			};

			float4 _CropColor;
			float4 _Crop;
			float _Fade;
			
			v2f vert (appdata v)
			{
				v2f o;
				v.vertex.xy *= _ScreenParams.xy;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.color = v.color;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				if (_Crop.w == 0) {
					float dis = distance(half2(_Crop.x, _Crop.y), i.vertex.xy);
					if (dis >_Crop.z){
						return i.color;
					}else{
						_CropColor.w = _CropColor.w-(_Crop.z-dis)/_Crop.z*_Fade;
						return _CropColor;
					}
				}
				else {
					bool isCrop = i.vertex.x > _Crop.x - _Crop.z / 2 && i.vertex.x < _Crop.x + _Crop.z / 2
						&& i.vertex.y > (_Crop.y - _Crop.w / 2) && i.vertex.y < _Crop.y + _Crop.w / 2;
						if (isCrop){
							_CropColor.w = 0;
							return _CropColor;
						}else{
							return i.color;
						}
				}
			}
			ENDCG
		}
	}
}


传入ugui世界坐标


using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;


    public class CropComponent : MaskableGraphic
    {
        private Transform canvasUIRoot;
        public Vector4 inner;

         public void UpdatePos(Vector4 m_inner)
        {

            if (canvasUIRoot == null)
            {
                GameObject uiRoot = GameObject.Find("Canvas");//overlay 
                if (uiRoot == null) return;
                canvasUIRoot = uiRoot.transform;
            }

            RectTransform rootRectTransform = canvasUIRoot.GetComponent<RectTransform>();

          
            var scaleScreen = rootRectTransform.sizeDelta;
            var xScale = scaleScreen.x / Screen.width;
            var yScale = scaleScreen.y / Screen.height;

            //Debug.LogFormat("inner-{0}",m_inner);

            //Debug.LogFormat("sw-{0} sh-{1}",xScale,yScale);

            inner.x = (m_inner.x);
            inner.y = (Screen.height-m_inner.y);
            inner.z = (m_inner.z ) / xScale;
            inner.w = (m_inner.w ) / yScale;

            this.material.SetVector("_Crop",inner);
        }

    }



Shader "UI/FadeCircle"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
		_Definition ("Definition", Vector) = (0, 0, 0, 0)

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile __ UNITY_UI_CLIP_RECT
            #pragma multi_compile __ UNITY_UI_ALPHACLIP

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

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

            sampler2D _MainTex;
            fixed4 _Color;
			float4 _Definition;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;

            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                OUT.color = v.color * _Color;
                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                #ifdef UNITY_UI_CLIP_RECT
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                #endif

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

				//color.a *= distance(IN.worldPosition.xy, _Definition.xy) > _Definition.z;
                float falloff = saturate(distance(IN.worldPosition.xy, _Definition.xy) * _Definition.z);
                color.a *= falloff;

				color.rbg *= color.a;

                return color;
            }
        ENDCG
        }
    }
}


扩展UGUI挖洞

image

重写OnPopulateMesh方法自行构造挖洞矩形三角面

image


using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

namespace Component.UI
{

    public class RectMaskComponent : MaskableGraphic
    {
        private Transform canvasUIRoot;
        public Vector4 inner;
        public Vector4 outer;


        public void UpdatePos(Vector4 m_inner)
        {

            if (canvasUIRoot == null)
            {
                GameObject uiRoot = GameObject.Find("Canvas");
                if (uiRoot == null) return;
                canvasUIRoot = uiRoot.transform;
            }

            RectTransform rootRectTransform = canvasUIRoot.GetComponent<RectTransform>();

            this.outer = new Vector4(-rootRectTransform.pivot.x * rootRectTransform.rect.width,
            -rootRectTransform.pivot.y * rootRectTransform.rect.height,
            (1 - rootRectTransform.pivot.x) * rootRectTransform.rect.width,
            (1 - rootRectTransform.pivot.y) * rootRectTransform.rect.height);

            var scaleScreen = rootRectTransform.sizeDelta;
            var xScale = scaleScreen.x / Screen.width;
            var yScale = scaleScreen.y / Screen.height;


            Vector2 size = new Vector2(m_inner.z * canvasUIRoot.transform.localScale.x, m_inner.w * canvasUIRoot.transform.localScale.y);
            inner.x = (m_inner.x - size.x / 2) * xScale - scaleScreen.x / 2;
            inner.y = (m_inner.y - size.y / 2) * yScale - scaleScreen.y / 2;
            inner.z = (m_inner.x + size.x / 2) * xScale - scaleScreen.x / 2;
            inner.w = (m_inner.y + size.y / 2) * yScale - scaleScreen.y / 2;

            this.SetVerticesDirty();
        }

        protected override void OnPopulateMesh(VertexHelper vh)
        {

            //Debug.Log(outer);
            // Debug.Log(inner);

            Color32 color32 = color;
            vh.Clear();

            //left
            vh.AddVert(new Vector2(outer.x, outer.y), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(outer.x, outer.w), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.x, outer.w), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.x, outer.y), color32, new Vector2(0f, 0f));

            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(0, 2, 3);

            //right
            vh.AddVert(new Vector2(inner.z, outer.y), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.z, outer.w), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(outer.z, outer.w), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(outer.z, outer.y), color32, new Vector2(0f, 0f));

            vh.AddTriangle(4, 5, 6);
            vh.AddTriangle(4, 6, 7);

            //clip
            vh.AddVert(new Vector2(inner.x, inner.y), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.x, inner.w), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.z, inner.y), color32, new Vector2(0f, 0f));
            vh.AddVert(new Vector2(inner.z, inner.w), color32, new Vector2(0f, 0f));

            //top
            vh.AddTriangle(9, 2, 5);
            vh.AddTriangle(9, 5, 11);

            //bottom
            vh.AddTriangle(3, 8, 10);
            vh.AddTriangle(3, 10, 4);

            return;
        }

    }
}

点击穿透处理

FilterGameObjectName为需要穿透点击的GameObject的名称


 public class ClickPenetrateComponent : MonoBehaviour, IPointerClickHandler
    {
        public string FilterGameObjectName = "";

        public void OnPointerClick(PointerEventData eventData)
        {
            //Call(eventData);
            PassEvent(eventData, ExecuteEvents.pointerClickHandler);
        }

        private void PassEvent<T>(PointerEventData data, ExecuteEvents.EventFunction<T> function) where T : IEventSystemHandler
        {
            var results = new List<RaycastResult>();
            UnityEngine.EventSystems.EventSystem.current.RaycastAll(data, results);
            var current = data.pointerCurrentRaycast.gameObject;
            for (int i = 0; i < results.Count; i++)
            {
                GameObject obj = results[i].gameObject;
                if (current != obj)
                {
                    if (obj.name == FilterGameObjectName)
                    {
                        ExecuteEvents.Execute(obj, data, function);
                    }
                }
            }
        }
}

毛玻璃效果1

GrabPass{} 抓取当前屏幕内容(_GrabTexture),在移动端每次使用的开销都非常昂贵

image


Shader "Unlit/FrostedGlass"
{
	Properties
	{
		_Radius("Radius", Range(1, 255)) = 1
	}

		Category
	{
		Tags{ "Queue" = "Transparent" }

		SubShader
	{
		GrabPass
	{
		
	}

		Pass
	{

		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#include "UnityCG.cginc"

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

	struct v2f
	{
		float4 vertex : POSITION;
		float4 uvgrab : TEXCOORD0;
	};

	v2f vert(appdata_t v)
	{
		v2f o;
		o.vertex = UnityObjectToClipPos(v.vertex);
		o.uvgrab = ComputeGrabScreenPos(o.vertex);
		return o;
	}

	sampler2D _GrabTexture;
	float4 _GrabTexture_TexelSize;
	float _Radius;

	half4 frag(v2f i) : COLOR
	{
		half4 sum = half4(0,0,0,0);

#define GRABXYPIXEL(kernelx, kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely, i.uvgrab.z, i.uvgrab.w)))

		sum += GRABXYPIXEL(0.0, 0.0);
		int measurments = 1;

		for (float range = 0.1f; range <= _Radius; range += 0.1f)
		{
			sum += GRABXYPIXEL(range, range);
			sum += GRABXYPIXEL(range, -range);
			sum += GRABXYPIXEL(-range, range);
			sum += GRABXYPIXEL(-range, -range);
			measurments += 4;
		}

		return sum / measurments;
	}
		ENDCG
	}

		GrabPass
	{

	}

	Pass
	{

		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

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

	struct v2f
	{
		float4 vertex : POSITION;
		float4 uvgrab : TEXCOORD0;
	};

	v2f vert(appdata_t v)
	{
		v2f o;
		o.vertex = UnityObjectToClipPos(v.vertex);
		o.uvgrab = ComputeGrabScreenPos(o.vertex);
		return o;
	}

	sampler2D _GrabTexture;
	float4 _GrabTexture_TexelSize;
	float _Radius;

	half4 frag(v2f i) : COLOR
	{
		half4 sum = half4(0,0,0,0);
		float radius = 1.41421356237 * _Radius;

#define GRABXYPIXEL(kernelx, kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely, i.uvgrab.z, i.uvgrab.w)))

		sum += GRABXYPIXEL(0.0, 0.0);
		int measurments = 1;

		for (float range = 1.41421356237f; range <= radius * 1.41; range += 1.41421356237f)
		{
			sum += GRABXYPIXEL(range, 0);
			sum += GRABXYPIXEL(-range, 0);
			sum += GRABXYPIXEL(0, range);
			sum += GRABXYPIXEL(0, -range);
			measurments += 4;
		}

		return sum / measurments;
	}
		ENDCG
	}



	}
	}
}

毛玻璃效果2

面板背景模糊效果

image

使用void OnRenderImage (RenderTexture source, RenderTexture destination) 抓取屏幕到renderTexture,然后高斯模糊后挂在Image上显示。

image

只抓取了一次屏幕,通过maxRenderTime控制。downsample越大renderTexture采样越小性能越好。


using UnityEngine;
using UnityEngine.UI;

namespace Aurora.Component.UI
{

    [RequireComponent(typeof(Camera))]
    public class BlurImageComponent : MonoBehaviour
    {
        public Material uiMaterial;
        public Material blurMaterial;

        [Range(0f, 8f)]
        public int kernel = 0;

        [Range(0f, 1f)]
        public float interpolation = 1f;

        [Range(0, 4)]
        public int downsample = 3;

        [Range(1, 8)]
        public int iterations = 1;

        public uint maxRenderTime = 1;
       

        private BlurComponent blurComponent;



        void Start(){
            if (!SystemInfo.supportsImageEffects)
            {
                enabled = false;
                return;
            }
            //post effect
            GameObject gameObject = GameObject.Find("Camera");
            if (gameObject != null){
                Camera mainCamera = gameObject.GetComponent<Camera>();
                if(mainCamera != null){
                    blurComponent = gameObject.AddComponent<BlurComponent>();
                    blurComponent.bindUI = this;
                }    
            }
            //render to image
            Image img = this.gameObject.GetComponent<Image>();
            if(img == null){
                img = this.gameObject.AddComponent<Image>();
            }
            img.material = uiMaterial;
        }

        void OnDestroy(){
            if (blurComponent != null){
                blurComponent.bindUI = null;
                GameObject.Destroy(blurComponent);
                blurComponent = null;
            }
        } 
    }

    [RequireComponent(typeof(Camera))]
    public class BlurComponent : MonoBehaviour
    {
        private uint curRenderTime = 0;
        private RenderTexture rt;
        public BlurImageComponent bindUI;
        public static readonly int _Radius = Shader.PropertyToID("_Radius");
        public static readonly int _BackgroundTexture = Shader.PropertyToID("_BlurTexture");

        protected void Blur(RenderTexture source, RenderTexture destination)
        {
            int tw = source.width >> bindUI.downsample;
			int th = source.height >> bindUI.downsample;


            var rt2 = RenderTexture.GetTemporary(tw, th, 0, source.format);

            for (int i = 0; i < bindUI.iterations; i++)
            {
               
                float radius = (float)i * bindUI.interpolation + bindUI.interpolation;
                bindUI.blurMaterial.SetFloat(_Radius, radius);

                Graphics.Blit(source, rt2, bindUI.blurMaterial, 1 + bindUI.kernel);
                source.DiscardContents();

                
                if (i == bindUI.iterations - 1)
                {
                    Graphics.Blit(rt2, destination, bindUI.blurMaterial, 2 + bindUI.kernel);
                }
                else
                {
                    Graphics.Blit(rt2, source, bindUI.blurMaterial, 2 + bindUI.kernel);
                    rt2.DiscardContents();
                }
            }

            RenderTexture.ReleaseTemporary(rt2);
        }

        void OnRenderImage (RenderTexture source, RenderTexture destination) 
		{
			if (bindUI == null || bindUI.blurMaterial == null || bindUI.uiMaterial == null) return;

            if (curRenderTime < bindUI.maxRenderTime){
                
                int tw = source.width >> bindUI.downsample;
                int th = source.height >> bindUI.downsample;

                rt = RenderTexture.GetTemporary(tw, th, 0, source.format);

                Graphics.Blit(source, rt);

                Blur(rt, rt);
                bindUI.uiMaterial.SetTexture(_BackgroundTexture, rt);
                Graphics.Blit(source, destination);

                curRenderTime++;
                if(curRenderTime == bindUI.maxRenderTime){
                    this.enabled = false;
                    return;
                }
                
                RenderTexture.ReleaseTemporary(rt);
                rt = null;
            }
		}

         void OnDestroy(){
            if (rt != null){
               RenderTexture.ReleaseTemporary(rt);
               rt = null;
            }
        } 

    }
}

高斯模糊shader

Shader "Unlit/BlurPostShader"
{
	Properties
	{
		_MainTex ("", 2D) = "white" {}
	}

	CGINCLUDE

	#include "UnityCG.cginc"

	struct vertexInput
	{
		float4 vertex : POSITION;
		float2 uv : TEXCOORD0;
	};

	struct vertexOutput
	{
		float4 vertex : SV_POSITION;
		float2 texcoord : TEXCOORD0;
	};

	struct output_tap
	{
		float4 vertex : SV_POSITION;
		float2 texcoord : TEXCOORD0;
		float4 blurTexcoord : TEXCOORD1;
	};


	uniform sampler2D _MainTex;
	uniform float4 _MainTex_ST;
	uniform float2 _MainTex_TexelSize;

	uniform float _Radius;


	vertexOutput vert (vertexInput IN)
	{
		vertexOutput OUT;

		OUT.vertex = UnityObjectToClipPos(IN.vertex);
		OUT.texcoord = IN.uv;

		return OUT;
	}

	fixed4 frag (vertexOutput IN) : SV_Target
	{
		fixed3 color = tex2D(_MainTex, IN.texcoord);
		return fixed4(color, 1.0);
	}


	output_tap vertHorizontal (vertexInput IN)
	{
		output_tap OUT;

		OUT.vertex = UnityObjectToClipPos(IN.vertex);

		float2 offset = float2(_MainTex_TexelSize.x * _Radius * 1.33333333, 0.0); 

		float2 uv = IN.uv;

		OUT.texcoord = uv;
		OUT.blurTexcoord.xy = uv + offset;
		OUT.blurTexcoord.zw = uv - offset;

		return OUT;
	}

	output_tap vertVertical (vertexInput IN)
	{
		output_tap OUT;

		OUT.vertex = UnityObjectToClipPos(IN.vertex);

		float2 offset = float2(0.0, _MainTex_TexelSize.y * _Radius * 1.33333333); 

		float2 uv = IN.uv;

		OUT.texcoord = uv;
		OUT.blurTexcoord.xy = uv + offset;
		OUT.blurTexcoord.zw = uv - offset;

		return OUT;
	}

	fixed4 fragBlur (output_tap IN) : SV_Target
	{
		fixed3 sum = tex2D(_MainTex, IN.texcoord).xyz * 0.29411764;
		sum += tex2D(_MainTex, IN.blurTexcoord.xy).xyz * 0.35294117;
		sum += tex2D(_MainTex, IN.blurTexcoord.zw).xyz * 0.35294117;
		return fixed4(sum, 1.0);
	}


	ENDCG

	SubShader
	{
		ZTest Always Cull Off ZWrite Off


		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}

		
		Pass
		{
			CGPROGRAM
			#pragma vertex vertHorizontal
			#pragma fragment fragBlur
			ENDCG
		}

		Pass
		{
			CGPROGRAM
			#pragma vertex vertVertical
			#pragma fragment fragBlur
			ENDCG
		}


	

	}
}


显示高斯模糊后renderTexture的shader

Shader "Unlit/BlurUIShader"
{
	Properties
	{
		_BlurTexture ("Background Texture", 2D) = "white" {}

		_Color ("Tint", Color) = (1,1,1,0)

	}

	SubShader
	{
		Tags
		{ 
			"Queue"="Transparent" 
			"IgnoreProjector"="True" 
			"RenderType"="Transparent" 
			"PreviewType"="Plane"
			"CanUseSpriteAtlas"="True"
		}


		Pass
		{
			Name "Default"
		CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0

			#include "UnityCG.cginc"
			#include "UnityUI.cginc"


			
			struct appdata_t
			{
				float4 vertex   : POSITION;
				float4 color    : COLOR;
			};

			struct v2f
			{
				float4 vertex   : SV_POSITION;
				fixed4 color    : COLOR;
				float4 grabPos  : TEXCOORD2;
			};
			
			fixed4 _Color;
			
			sampler2D _BlurTexture;
			float2 _BlurTexture_TexelSize;

			v2f vert(appdata_t IN)
			{
				v2f OUT;
				OUT.vertex = UnityObjectToClipPos(IN.vertex);

			#ifdef UNITY_HALF_TEXEL_OFFSET
				OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1) * OUT.vertex.w;
			#endif
				//计算该模型顶点在屏幕坐标的纹理信息
				OUT.grabPos = ComputeGrabScreenPos(OUT.vertex);

			#if UNITY_UV_STARTS_AT_TOP
				OUT.grabPos.y = 1.0 - OUT.grabPos.y;
			#endif

				OUT.color = IN.color * _Color;
				return OUT;
			}

			fixed4 frag(v2f IN) : SV_Target
			{
				half3 bgcolor = tex2Dproj(_BlurTexture, IN.grabPos).rgb * IN.color;
				return half4(bgcolor, 1.0);
			}
		ENDCG
		}
	}
}