1. 程式人生 > >【Unity】用Shader實現圖片的區域遮罩,支援半透明,實現地圖動態上色功能

【Unity】用Shader實現圖片的區域遮罩,支援半透明,實現地圖動態上色功能

一個專案,做世界地圖時,希望未開啟的地塊是線稿,新地塊開啟時,做一個上色處理。

想到的方案就是:上了色的彩圖蓋線上稿上,然後用mask 控制彩圖的區域性顯隱。

網上找了一個,可以半透明遮罩的shader:
https://www.jianshu.com/p/1d9d439c28fa

要控制不同區塊顯示或不顯示,要怎麼處理呢?  mask圖其實只用到了 alpha,還有rgb24位可以用。

於是自己改了一下shader,對mask圖有特殊要求,rgb顏色必須是 1 2 4 8 16 32 64 128 就可以用位操作組合最多控制24個圖塊了。

有一個難點在,shader怎麼對rgb色值位操作,  找了一下發現  (int)round(color.r*255.0) 轉成整數可以


Shader "ImageEffect/AlphaMask"
{
	Properties
	{
		[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
		_Mask("Base (RGB)", 2D) = "white" {}			//遮罩圖
		_AreaMask("AreaMask", Color) = (1,0,0,0)		//區塊顯隱配置(可完全顯示的),按 r g b位運算,最多24塊區域

		_AlphaVal("AlphaVal", Range(0,5)) = 1.0			//控制動態顯示的變化值,乘以 mask.color.a, 讓其從強到弱慢慢顯示出來
		_AnimAreaMask("AnimAreaMask", Color) = (1,0,0,0)	//當前要隨AlphaVal變化而顯示變化過程的區塊配置,按 r g b位運算,最多24塊區域
		_AlphaThreshold("AlphaThreshold",float) = 0.4 //mask的alpha乘以AlphaVal後 顯隱閾值,不到則不顯示



		_Color("Tint", Color) = (1,1,1,1)
		_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
		{
			CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag

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

	#pragma multi_compile __ UNITY_UI_ALPHACLIP

			struct a2v
			{
				fixed2 uv : TEXCOORD0;
				half4 vertex : POSITION;
				float4 color    : COLOR;
			};


			struct v2f
			{
				fixed2 uv : TEXCOORD0;
				half4 vertex : SV_POSITION;
				float4 color    : COLOR;
			};

			sampler2D _MainTex;
			sampler2D _Mask;

			float _AlphaVal;

			fixed4 _Color;
			fixed4 _AreaMask;
			fixed4 _AnimAreaMask;
			float _AlphaThreshold;

			v2f vert(a2v i)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, i.vertex);
				o.uv = i.uv;

				o.color = i.color * _Color;
				return o;
			}

			fixed4 frag(v2f i) : COLOR
			{
				half4 color = tex2D(_MainTex, i.uv) * i.color;
				half4 mask = tex2D(_Mask, i.uv);
				//color.a *= mask.a;  //原半透明遮罩演算法

				int maskr = round(mask.r * 255.0);
				int maskg = round(mask.g * 255.0);
				int maskb = round(mask.b * 255.0);
				int t = ((int)round(_AreaMask.r * 255.0) & maskr)
					| ((int)round(_AreaMask.g * 255.0) & maskg)
					| ((int)round(_AreaMask.b * 255.0) & maskb); //mask 和 配置的顏色塊進行位運算,> 0表示符合開啟條件
				int t2 = ((int)round(_AnimAreaMask.r * 255.0) & maskr)
					| ((int)round(_AnimAreaMask.g * 255.0) & maskg)
					| ((int)round(_AnimAreaMask.b * 255.0) & maskb); //mask 和 配置的顏色塊進行位運算,> 0表示符合開啟條件
				float ma = mask.a * _AlphaVal;
				color.a *= step(1, t)*mask.a*5 + step(1, t2) * ((ma - _AlphaThreshold) * 5 * step(_AlphaThreshold, ma));
				/* 上面的算式相當於下面的邏輯判斷
				if(t > 0)
				{
					color.a *= mask.a*5;	//顯示這個區域 (或者 mask.a==0時不顯示)
				}
				else if(t2 > 0)				//逐漸顯示的區域
				{
					if (ma > _AlphaThreshold)	//超過閾值才顯示
					{
						color.a *= (ma - _AlphaThreshold) * 5;  //color.a *= ma 邊緣會有明顯輪廓,改進了一下演算法,讓邊緣柔和些。5可以隨便調整一下
					}
					else
					{
						color.a = 0;
					}
				}
				else
				{
					color.a = 0;
				} */
				
				return color;
			}
			ENDCG
		}
	}
}

不知道效率如何。

用到的測試mask圖是:

編號和rgb對應關係:
0: r1 g0 b0
5: r32 g0 b0
7: r128 g0 b0
13: r0 g32 b0
20: r0 g0 b16

AreaMask 的顏色控制了 直接顯示的色塊 比如 填 (33,0,0),則 0、5塊圖就會顯示出來。
AnimAreaMask 的顏色控制哪些圖塊需要 隨 AlphaVal變化而動態顯示出來。

用程式設定這兩個顏色,然後調整 AlphaVal就能實現策劃需要的功能了,至於具體效果嘛,還要美術把對應的圖做好 :P


測試專案的下載地址:
http://download.csdn.net/download/zhenmu/10244500