WorldSpace 2D Grid Shader With Layered Pop-Up Tint
世界坐标系下的,带有分层弹出 Tint 的 2D 网格着色器
基础 Tint
Shader "Unlit/UnlitGrid"
{
Properties
{
[PerRendererData] _MainTex ("Texture", 2D) = "white" {}
_GridSize ("Grid Size", float) = 1
[FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
_LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
_ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
_UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)
_TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
_TintRadius("Tint Radius", float) = 1
_TintPower("Tint Power", float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 worldSpacePos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _GridSize;
float _GridLineSize;
float4 _LineColor;
float4 _ReachableTint;
float4 _UnreachableTint;
vector _TintCenter;
float _TintRadius;
float _TintPower;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
{
// tint by ipos
float2 ipos_to_tint_center = ipos - floor(_TintCenter/_GridSize);
float dist_ipos_to_tint_center = abs(ipos_to_tint_center.x) + abs(ipos_to_tint_center.y);
float reachable = step(dist_ipos_to_tint_center, _TintRadius + 0.001);
fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
col = lerp(col, tint, _TintPower);
return col;
}
fixed4 add_grid(fixed4 col, float2 fpos)
{
float scaled_line_size = _GridLineSize/_GridSize;
float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos); // bottom-left
float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos); // top-right
float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;
col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.worldSpacePos.xy / _GridSize;
float2 ipos = floor(st); // integer
float2 fpos = frac(st); // fraction
fixed4 col = tex2D(_MainTex, i.uv);
col = add_tint(col, ipos, fpos);
col = add_grid(col, fpos);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
这个效果是点一下就有一个范围出现,但是没有动画过程
基础移动 Tint
假设我需要一个从中心往外移动的过程
Shader "Unlit/UnlitGrid"
{
Properties
{
[PerRendererData] _MainTex ("Texture", 2D) = "white" {}
_GridSize ("Grid Size", float) = 1
[FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
_LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
_ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
_UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)
_TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
_TintRadius("Tint Radius", float) = 1
_TintPower("Tint Power", float) = 0
[FloatRange] _TintGridRadius("Tint Grid Radius", Range(0, 0.5)) = 0
[FloatRange] _TintGridCenterOffset("Tint Grid Center Offset", Range(0, 0.5)) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 worldSpacePos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _GridSize;
float _GridLineSize;
float4 _LineColor;
float4 _ReachableTint;
float4 _UnreachableTint;
vector _TintCenter;
float _TintRadius;
float _TintPower;
float _TintGridRadius;
float _TintGridCenterOffset;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
{
// tint by ipos
float2 ipos_to_tint_center = ipos - floor(_TintCenter/_GridSize);
float dist_ipos_to_tint_center = abs(ipos_to_tint_center.x) + abs(ipos_to_tint_center.y);
float reachable = step(dist_ipos_to_tint_center, _TintRadius + 0.001);
fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
// tint by fpos
// move dir
// add 0.1f to avoid 0/0 = inf
float2 move_dir = ipos_to_tint_center/(length(ipos_to_tint_center) + 0.1f);
float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - _TintGridCenterOffset);
float2 fpos_to_grid_center = fpos - tint_grid_center;
float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
float tintable = step(dist_fpos_to_grid_center, _TintGridRadius) * reachable;
col = lerp(col, lerp(col, tint, _TintPower), tintable);
return col;
}
fixed4 add_grid(fixed4 col, float2 fpos)
{
float scaled_line_size = _GridLineSize/_GridSize;
float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos); // bottom-left
float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos); // top-right
float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;
col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.worldSpacePos.xy / _GridSize;
float2 ipos = floor(st); // integer
float2 fpos = frac(st); // fraction
fixed4 col = tex2D(_MainTex, i.uv);
col = add_tint(col, ipos, fpos);
col = add_grid(col, fpos);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
用法:在棋盘的脚本中,设置初始值和 Tween
render.material.SetVector("_TintCenter", new Vector4(transform.position.x, transform.position.y, 0f, 0f));
render.material.SetFloat("_TintRadius", reachableSteps);
render.material.SetFloat("_TintPower", 0f);
render.material.SetFloat("_TintGridRadius", 0f);
render.material.SetFloat("_TintGridCenterOffset", 0f);
render.material.DOFloat(1f, "_TintPower", 0.3f);
render.material.DOFloat(0.5f, "_TintGridRadius", 0.3f);
render.material.DOFloat(0.5f, "_TintGridCenterOffset", 0.3f);
效果如图,确实有一点移动了
分层移动 Tint
但是其实这个移动是所有方块都同时移动,看上去没有层次
所以应该使用一个递增的变量来控制先后移动
由 st 的整数部分和小数部分的计算启发,不如使用一个时间的整数部分作为第几层,时间的小数部分作为每一层内的移动
Shader "Unlit/UnlitGrid"
{
Properties
{
[PerRendererData] _MainTex ("Texture", 2D) = "white" {}
_GridSize ("Grid Size", float) = 1
[FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
_LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
_ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
_UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)
_TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
_TintRadius("Tint Radius", float) = 1
_TintPower("Tint Power", float) = 0
_TintSpreadTime("Tint Spread Time", float) = 0
_TintSpreadSpeed("Tint Spread Speed", float) = 5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 worldSpacePos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _GridSize;
float _GridLineSize;
float4 _LineColor;
float4 _ReachableTint;
float4 _UnreachableTint;
vector _TintCenter;
float _TintRadius;
float _TintPower;
float _TintSpreadTime;
float _TintSpreadSpeed;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
{
float itime = floor(_TintSpreadTime * _TintSpreadSpeed); // integer time
float ftime = frac(_TintSpreadTime * _TintSpreadSpeed); // fraction time
float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
float dist_tint_center_to_ipos = abs(tint_center_to_ipos.x) + abs(tint_center_to_ipos.y);
float stationary_reachable = step(dist_tint_center_to_ipos, itime - 1.0 + 0.001);
float spreading_reachable = step(dist_tint_center_to_ipos, itime + 0.001) - step(dist_tint_center_to_ipos, itime - 1.0 + 0.001);
// move
float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);
float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);
// scale
float2 fpos_to_grid_center = fpos - tint_grid_center;
float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);
// add tint
float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
return lerp(col, tint, _TintPower);
}
fixed4 add_grid(fixed4 col, float2 fpos)
{
float scaled_line_size = _GridLineSize/_GridSize;
float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos); // bottom-left
float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos); // top-right
float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;
col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.worldSpacePos.xy / _GridSize;
float2 ipos = floor(st); // integer
float2 fpos = frac(st); // fraction
fixed4 col = tex2D(_MainTex, i.uv);
col = add_tint(col, ipos, fpos);
col = add_grid(col, fpos);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
用法:
private void OnMouseDown()
{
isDraging = true;
spreadTime = 0f;
Chessboard.Singleton.render.material.SetVector("_TintCenter", new Vector4(transform.position.x, transform.position.y, 0f, 0f));
Chessboard.Singleton.render.material.SetFloat("_TintRadius", reachableSteps);
Chessboard.Singleton.render.material.SetFloat("_TintPower", 0f);
Chessboard.Singleton.render.material.SetFloat("_TintSpreadTime", spreadTime);
Chessboard.Singleton.render.material.DOFloat(1f, "_TintPower", 0.3f);
}
protected void Update()
{
if (!isDraging)
return;
spreadTime += Time.deltaTime;
Chessboard.Singleton.render.material.SetFloat("_TintSpreadTime", spreadTime);
}
private void OnMouseUp()
{
isDraging = false;
}
效果
现在分层是有了,但是移动是线性变化的,看上去一般
Ease 曲线分层移动 Tint
所以还可以使用一些 ease 曲线
Shader "Unlit/UnlitGrid"
{
Properties
{
[PerRendererData] _MainTex ("Texture", 2D) = "white" {}
_GridSize ("Grid Size", float) = 1
[FloatRange] _GridLineSize ("Grid Line Size", Range(0, 1)) = 0.02
_LineColor ("Line Color", Color) = (0.7, 0.7, 0.7, 1)
_ReachableTint ("Reachable Tint", Color) = (0.9, 0.9, 0.9, 1)
_UnreachableTint ("Unreachable Tint", Color) = (0.6, 0.6, 0.6, 1)
_TintCenter("Tint Center", Vector) = (0, 0, 0, 0)
_TintRadius("Tint Radius", float) = 1
_TintPower("Tint Power", float) = 0
_TintSpreadTime("Tint Spread Time", float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 worldSpacePos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _GridSize;
float _GridLineSize;
float4 _LineColor;
float4 _ReachableTint;
float4 _UnreachableTint;
vector _TintCenter;
float _TintRadius;
float _TintPower;
float _TintSpreadTime;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 add_tint(fixed4 col, float2 ipos, float2 fpos)
{
float itime = floor(_TintSpreadTime); // integer time
float ftime = frac(_TintSpreadTime); // fraction time
float2 tint_center_to_ipos = ipos - floor(_TintCenter/_GridSize);
float dist_tint_center_to_ipos = abs(tint_center_to_ipos.x) + abs(tint_center_to_ipos.y);
float stationary_reachable = step(dist_tint_center_to_ipos, itime + 0.001);
float spreading_reachable = step(dist_tint_center_to_ipos, itime + 1.001) - step(dist_tint_center_to_ipos, itime + 0.001);
// move
float2 move_dir = tint_center_to_ipos/(length(tint_center_to_ipos) + 0.1f);
float2 tint_grid_center = float2(0.5, 0.5) - move_dir * (0.5f - ftime/2.0);
// scale
float2 fpos_to_grid_center = fpos - tint_grid_center;
float dist_fpos_to_grid_center = max(abs(fpos_to_grid_center.x), abs(fpos_to_grid_center.y));
spreading_reachable *= step(dist_fpos_to_grid_center, ftime/2.0);
// add tint
float radius_reachable = step(dist_tint_center_to_ipos, _TintRadius + 0.001);
float reachable = (stationary_reachable + spreading_reachable) * radius_reachable;
fixed4 tint = col * _UnreachableTint * (1.0 - reachable) + col * _ReachableTint * reachable;
return lerp(col, tint, _TintPower);
}
fixed4 add_grid(fixed4 col, float2 fpos)
{
float scaled_line_size = _GridLineSize/_GridSize;
float2 bl = step(float2(scaled_line_size, scaled_line_size), fpos); // bottom-left
float2 tr = step(float2(scaled_line_size, scaled_line_size), float2(1.0, 1.0) - fpos); // top-right
float grid_brightness = 1.0 - bl.x * bl.y * tr.x * tr.y;
col = col * (1.0 - grid_brightness) + _LineColor * grid_brightness;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.worldSpacePos.xy / _GridSize;
float2 ipos = floor(st); // integer
float2 fpos = frac(st); // fraction
fixed4 col = tex2D(_MainTex, i.uv);
col = add_tint(col, ipos, fpos);
col = add_grid(col, fpos);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
用法:
private void OnMouseDown()
{
tintSeq.Pause();
tintSeq = DOTween.Sequence();
tintSeq.Insert(0f, Chessboard.Singleton.render.material.DOFloat(1f, "_TintPower", 0.3f));
tintSeq.Insert(0f, Chessboard.Singleton.render.material
.DOFloat(reachableSteps, "_TintSpreadTime", 1f)
.SetEase(Ease.OutQuint));
Chessboard.Singleton.render.material.SetVector("_TintCenter", new Vector4(transform.position.x, transform.position.y, 0f, 0f));
Chessboard.Singleton.render.material.SetFloat("_TintRadius", reachableSteps);
Chessboard.Singleton.render.material.SetFloat("_TintPower", 0f);
Chessboard.Singleton.render.material.SetFloat("_TintSpreadTime", 0f);
tintSeq.Play();
}
效果: