新手引导需要处理的几种效果
shader处理挖洞
挖矩形洞
挖圆形洞
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挖洞
重写OnPopulateMesh方法自行构造挖洞矩形三角面
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),在移动端每次使用的开销都非常昂贵
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
面板背景模糊效果
使用void OnRenderImage (RenderTexture source, RenderTexture destination) 抓取屏幕到renderTexture,然后高斯模糊后挂在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
}
}
}