Posts Tagged Shader
Blur filter for UITexture in NGUI
SHADDERRRRS! Well that is it, I’m caught in shader land. The thing is, I don’t know jack **** about them. What I am missing mostly from Flash in Unity are filters like blur, drop shadow and glow. How you would go about doing that in Unity would be with shaders (I guess), so I stuck my nose in them. Here is my first version of a Blur filter/shader.
Shader "Unlit/Transparent Colored Blurred" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {} _Distance ("Distance", Float) = 0.015 } SubShader { LOD 100 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vertexProgram #pragma fragment fragmentProgram #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; float2 textureCoordinate : TEXCOORD0; fixed4 color : COLOR; }; struct vertexToFragment { float4 vertex : SV_POSITION; half2 textureCoordinate : TEXCOORD0; fixed4 color : COLOR; }; sampler2D _MainTex; float4 _MainTex_ST; float _Distance; vertexToFragment vertexProgram (appdata_t vertexData) { vertexToFragment output; output.vertex = mul(UNITY_MATRIX_MVP, vertexData.vertex); output.textureCoordinate = TRANSFORM_TEX(vertexData.textureCoordinate, _MainTex); output.color = vertexData.color; return output; } fixed4 fragmentProgram (vertexToFragment input) : COLOR { float distance = _Distance; fixed4 computedColor = tex2D(_MainTex, input.textureCoordinate) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x + distance , input.textureCoordinate.y + distance )) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x + distance , input.textureCoordinate.y)) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x , input.textureCoordinate.y + distance )) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x - distance , input.textureCoordinate.y - distance )) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x + distance , input.textureCoordinate.y - distance )) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x - distance , input.textureCoordinate.y + distance )) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x - distance , input.textureCoordinate.y)) * input.color; computedColor += tex2D(_MainTex, half2(input.textureCoordinate.x , input.textureCoordinate.y - distance )) * input.color; computedColor = computedColor / 9; return computedColor; } ENDCG } } } |
It works ok, with a lot of restrictions. First, you can only use it for NGUI UITextures. I would love it to work with UISPrites, but they all share the same atlas, so if you blur one sprite you blur them all! Also, for now, you should leave a padding of 10 transparent pixels around your texture for a better effect.
The Distance parameter is relative to the size of your texture so correct values will change from one texture to the other. Anyway you have it, I will keep working on it, but if you see anything that can be improved, don’t be afraid to tell me, I would really like to make it better.
Updated version of the Masking shader for NGUI
I did a shader before to mask a texture so that it doesn’t have to be rectangular. Turns out I was using an old version of NGUI, so when I updated (to version 2.65) my previous shader didn’t work anymore. Also Nicki Thomas Hansen made another shader so that you could use the masked texture inside a clipped panel. In doing so he also explained what NGUI was doing and how it was selecting the correct shader. So, based on his AlphaClip, I remade my shader so that it works on the new version of NGUI. Here is the code for it:
Shader "Unlit/Transparent Colored Masked" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {} _AlphaTex ("MaskTexture", 2D) = "white" {} } SubShader { LOD 100 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vertexProgram #pragma fragment fragmentProgram #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; float2 textureCoordinate : TEXCOORD0; fixed4 color : COLOR; }; struct vertexToFragment { float4 vertex : SV_POSITION; half2 textureCoordinate : TEXCOORD0; fixed4 color : COLOR; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _AlphaTex; vertexToFragment vertexProgram (appdata_t vertexData) { vertexToFragment output; output.vertex = mul(UNITY_MATRIX_MVP, vertexData.vertex); output.textureCoordinate = TRANSFORM_TEX(vertexData.textureCoordinate, _MainTex); output.color = vertexData.color; return output; } fixed4 fragmentProgram (vertexToFragment input) : COLOR { fixed4 computedColor = tex2D(_MainTex, input.textureCoordinate) * input.color; fixed4 alphaGuide = tex2D(_AlphaTex, input.textureCoordinate); if (alphaGuide.a < computedColor.a) computedColor.a = alphaGuide.a; return computedColor; } ENDCG } } } |
Funny how writing my previous post solved my future problem by someone else writing an answer post.
I will update the previous post so that it points to this post also.
UPDATE: I renamed all the variables to something readable, I thought it might be useful to understand what it is doing.
Masking textures using shaders NGUI
If you follow this blog, you know that I am having some problems with Unity3d and NGUI. Mostly it’s because I was so familiar with Flash/AS3 that I am feeling kinda lost. But I am getting better at this 2D in a 3D world thing. One of the thing that I miss the most is masks. In Flash they are very easy to use and with them you can do a plethora of effects and animations. Now with bitmap based technologies, it is not such a simple task to implement a mask.
Clipping
NGUI Panels have the option of being clipped panels, which means that only a rectangle of the panel will be shown. This is great for some cases, like when you need your masked region to be a rectangle, but for most masking cases it won’t work. Also, it doesn’t allow nested clipping which is a bummer.
Using another camera
Also, this guy created a shader that allows you to do similar masking as in Flash. It looks good, and it does the desired effect, but there is one drawback, for every mask, you need a new camera… That makes it very hard to manage in a large project or if you have multiple masks. I would use clipping more than this technique because it is easier to deal with.
Transparency shader
Now, this is the technique I devised that allows you to have multiple textures masked at the same time each with their own masks. This is really good if you load images (thumbnails) from a server and need them to be masked.
To do it we need to create a new shader. We start that by taking the Unlit – Transparent Colored shader and we will add two lines of code to it. First we will give it another texture for input. Secondly, we will take the output of the shader, use its rgb colors, but use the alpha of the new input texture we added. Here is the code :
Shader "Unlit/Transparent Colored with mask" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {} _AlphaTex ("Yeahyeah", 2D) = "white" {} } SubShader{ LOD 100 Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 ColorMask RGB AlphaTest Greater .01 Blend SrcAlpha OneMinusSrcAlpha ColorMaterial AmbientAndDiffuse SetTexture [_MainTex] { Combine Texture * Primary } SetTexture [_AlphaTex] { Combine previous, texture } } } } |
So that is the shader, but now we have to use it. This is actually what I found to be the most difficult part because there is a lot of documentation about how to make shaders, but not how to use them. So in the next chunk of code, we will create a texture in NGUI, give it a shader. After that we will feed the shader the textures it need to calculate the mask.
_newTexture = NGUITools.AddWidget<UITexture>(gameObject); _newTexture.pivot = UIWidget.Pivot.TopLeft; _newTexture.material = new Material(Shader.Find("Unlit/Transparent Colored with mask")); _newTexture.mainTexture = myTexture2D; _newTexture.MakePixelPerfect(); //now we give the shader the textures _newTexture.material.SetTexture(<wbr />"_MainTex", testRed); _newTexture.material.SetTexture(<wbr />"_AlphaTex", testAlpha); |
In this testRed is the image we want to mask and testAlpha is the alpha channel we want our previous image to use.
So here you have it, I will add pictures later to illustrate it better, but for now that’s how it is. Note that with this technique you can’t really animate or nest the masks, but you can have a lot of them at the same time.
UPDATE : If you are using a version of NGUI that is higher than 2.64, you should probably use this shader instead.