Unity Shader实现图片的区域遮罩,支持半透明,实现地图动态上色功能
发表于2019-01-16
做项目中的世界地图时,希望未开启的地块是线稿,新地块开启时,做一个上色处理。
想到的方案就是:上了色的彩图盖在线稿上,然后用mask 控制彩图的局部显隐。
参考了这个,可以半透明遮罩的shader:使用透明度实现Mask遮罩的Unity Shader
要控制不同区块显示或不显示,要怎么处理呢?? 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就能实现策划需要的功能了,至于具体效果嘛,还要美术把对应的图做好。