【Unity】用Shader實現圖片的區域遮罩,支援半透明,實現地圖動態上色功能
阿新 • • 發佈:2018-11-03
一個專案,做世界地圖時,希望未開啟的地塊是線稿,新地塊開啟時,做一個上色處理。
想到的方案就是:上了色的彩圖蓋線上稿上,然後用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