Hey Guys, I am new to shading but somehow managed to acomplish this for the new 2D Sprite system of Unity. You can use sliders to control the grayscalevalue If someone needs grayscale for his textures there you go: Code (csharp): Shader "Sprites/GrayScale" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 _EffectAmount ("Effect Amount", Range (0, 1)) = 1.0 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile DUMMY PIXELSNAP_ON #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; half2 texcoord : TEXCOORD0; }; fixed4 _Color; v2f vert(appdata_t IN) { v2f OUT; OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex); OUT.texcoord = IN.texcoord; OUT.color = IN.color * _Color; #ifdef PIXELSNAP_ON OUT.vertex = UnityPixelSnap (OUT.vertex); #endif return OUT; } sampler2D _MainTex; uniform float _EffectAmount; fixed4 frag(v2f IN) : COLOR { half4 texcol = tex2D (_MainTex, IN.texcoord); texcol.rgb = lerp(texcol.rgb, dot(texcol.rgb, float3(0.3, 0.59, 0.11)), _EffectAmount); texcol = texcol * IN.color; return texcol; } ENDCG } } Fallback "Sprites/Default" } If you want to change things threw code use this: Code (csharp): _GAMEOBJECT_.renderer.material.SetFloat("_EffectAmount", 0.5f); Cheers
This is great! I'm wondering though if it's possible to modify the code so that parts of the original color gets permanently revealed based on a circle around some coordinates? Similar to how a fog of war shader would work, I would imagine? Similar to this: http://codepen.io/cwolves/pen/prvnb
Thanks a lot Dev6_RC, I was using a shader that was working well on unity editor, but on android the shader was broken, I don't know why, but the yours worked really good! Thank you a lot!
This is really awesome! But I would like to have the same shader effect on regular textures but I'm really bad at shaders...anyone have any clue?
This is very cool and works great, but it doesn't seem to work with masking in the UI. I applied a material with this shader to a sprite on my canvas with a mask on top of it .. and the grayscale texture doesnt get masked. If I change material to none it masks just fine. Is this something that can be added?
I am sorry, but I have no time ATM. If i had to guess: this shader is based on the Unity sprites shader ( with some modifications ) You could try to look into the base shader of the unity UI system and add the required features into this one. If you do so feel free to post it here! Help the Community!
Thanks, this was really helpful. However, as noted by azmundai, it doesn't work with Unity UI's Image, so I used your shader to modify Unity's default shader and came up with this: Code (CSharp): Shader "Grayscale" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _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 _GrayscaleAmount ("Grayscale Amount", Range (0, 1)) = 1.0 [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 appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; half2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; }; fixed4 _Color; fixed4 _TextureSampleAdd; float4 _ClipRect; uniform float _GrayscaleAmount; v2f vert(appdata_t IN) { v2f OUT; OUT.worldPosition = IN.vertex; OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition); OUT.texcoord = IN.texcoord; #ifdef UNITY_HALF_TEXEL_OFFSET OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1); #endif OUT.color = IN.color * _Color; return OUT; } sampler2D _MainTex; fixed4 frag(v2f IN) : SV_Target { half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); color.rgb = lerp(color.rgb, dot(color.rgb, float3(0.3, 0.59, 0.11)), _GrayscaleAmount); #ifdef UNITY_UI_ALPHACLIP clip (color.a - 0.001); #endif return color; } ENDCG } } } Hope it helps!
Waow guys, it's awesome ! Thank you very much for these two Grey Scale shader scripts. There are very useful !
Just what I needed! Thanks @Dev6_RC! FUN FACT: I was curious about the magic vector (0.3, 0.59, 0.11) in this line: Code (cg): color.rgb= lerp(color.rgb, dot(color.rgb, float3(0.3, 0.59, 0.11)), _GrayscaleAmount); ...they're saying something like "make the grayscale whiteness value of a color 0.3R + 0.59G + 0.11B" but where do those coefficients come from? I looked it up and it seems they come from the NTSC TV standard/the way human eyes work: ~60% of perceived luminosity comes from green, ~30% from red, ~10% from blue.
Hey harleywinks Thanks for your kind reply! This happened a long time ago, but if I remember corectly this was the base Vector unity used in it's Sprite Shader. I used it to modify the color so the "real" Color and the "Grayscaled" Color look best
I made a Sprite/Diffuse version from the builtin shader & the grayscale version: Code (CSharp): // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt) Shader "Sprites/GrayScale" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1) [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1) [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 _EffectAmount ("Effect Amount", Range (0, 1)) = 1.0 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha CGPROGRAM #pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing #pragma multi_compile _ PIXELSNAP_ON #pragma multi_compile _ ETC1_EXTERNAL_ALPHA #include "UnitySprites.cginc" struct Input { float2 uv_MainTex; fixed4 color; }; void vert (inout appdata_full v, out Input o) { v.vertex.xy *= _Flip.xy; #if defined(PIXELSNAP_ON) v.vertex = UnityPixelSnap (v.vertex); #endif UNITY_INITIALIZE_OUTPUT(Input, o); o.color = v.color * _Color * _RendererColor; } uniform float _EffectAmount; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = SampleSpriteTexture (IN.uv_MainTex) * IN.color; c.rgb = lerp(c.rgb, dot(c.rgb, float3(0.3, 0.59, 0.11)), _EffectAmount); o.Albedo = c.rgb * c.a; o.Alpha = c.a; } ENDCG } Fallback "Sprite/Diffuse" }
Assign the material to the sprite at runtime, which will create a material instance. Add the [PerRendererData] attribute to the _EffectAmount property in the shader, then you can change each sprite at runtime without instantiating the material: Code (CSharp): public float effectAmount; private MaterialPropertyBlock propertyBlock; private SpriteRenderer spriteRenderer; private void Awake() { spriteRenderer = GetComponent<SpriteRenderer>(); propertyBlock = new MaterialPropertyBlock(); } private void Start() { if(spriteRenderer != null) { spriteRenderer.GetPropertyBlock(propertyBlock); propertyBlock.SetFloat("_EffectAmount", effectAmount); spriteRenderer.SetPropertyBlock(propertyBlock); } }
You can use a MaterialPropertyBlock to change indivual instances without instancing the material multiple times.
That's true. You'll also need to give the "_EffectAmount" the [PerRendererData] attribute I believe. I'll edit my example for that method since it's definitely better.
Since the release of shader graph I want to convert this shader to shader graph and if nescessary create the missing nodes. To all grayscale versions the r here even for the albedo.
I was trying this one and I am trying to change the _GrayscaleAmount in an Update function....But it doesn't update....the value changes on the material [I checked it on the inspector] but the UI image stays the same....the change happens when I disable and enable the object on which the material is attached to. Any solution would be really helpful please.
Ah, thank you so much! I've been looking for the vertex/fragment functions for a sprite shader for a while now, maybe I'm just clueless and couldn't find them, but either way this is a huge help
Hello, Can anyone please help me for UI masking support on this shader? As I am not able to mask my image. I tried other shader too but not getting instant reflection on image at runtime. So I must have to use this shader.!
Hello If anyone here looking for gray scale shader with masking support then here you go... Just create material in Assets\Resources\Materials\GrayscaleEffect.mat then apply grayscaleffectapplier on image gameobject Thank me later..
I have a scene where I want to dim the lights and I noticed that the default sprite shader was emitting light and thus lighting my objects. I was hoping this shader would fix that, but I see that this shader also still emits light. Can anyone explain why this is and where I can fix this (for 3d materials I know that there is a property emission)? Or point me to a sprite material or shader that does not emit light?
Doesnt work in my 2019, can some one fix it? I've found this on web, it works but lacking alpha control... I have no idea how to fix it, so can anyone take a look? Code (csharp): Shader "Unity3dTips/GrayscaleTransparent" { Properties{ _MainTex("Texture", 2D) = "white" {} _Color("Color", Color) = (1,1,1,1) } SubShader{ GrabPass { "_BackgroundTexture" } Pass { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } ZWrite Off ZTest Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _BackgroundTexture; sampler2D _MainTex; fixed4 _MainTex_ST; struct v2f { fixed4 vertex : SV_POSITION; fixed4 grabUV : TEXCOORD1; }; struct appdata { fixed4 vertex : POSITION; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.grabUV = ComputeGrabScreenPos(o.vertex); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 bgc = tex2Dproj(_BackgroundTexture, i.grabUV); return (bgc.r + bgc.g + bgc.b) / 3; } ENDCG } } FallBack Off }
Wow amazing! I was looking for such shader. It works great, thanks! I'm going to use this for mobile. I wonder if it has major performance effect for low-end mobile devices.
For all of you guys looking for the alpha control, I've modified the shader a bit so the alpha will control the amount of the greyscale effect: Code (CSharp): Shader "UI/GrayscaleTransparent" { Properties { _MainTex("Texture", 2D) = "white" {} _Color("Color", Color) = (1,1,1,1) } SubShader { GrabPass { "_BackgroundTexture" } Pass { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha ZTest Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _BackgroundTexture; sampler2D _MainTex; fixed4 _MainTex_ST; fixed4 _Color; struct v2f { fixed4 vertex : SV_POSITION; fixed4 color : COLOR; fixed4 grabUV : TEXCOORD1; }; struct appdata { fixed4 vertex : POSITION; fixed4 color : COLOR; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.grabUV = ComputeGrabScreenPos(o.vertex); o.color = v.color * _Color; return o; }; fixed4 frag(v2f i) : SV_Target { fixed4 texcol = tex2Dproj(_BackgroundTexture, i.grabUV); texcol.rgb = lerp(texcol.rgb, dot(texcol.rgb, float3(0.3, 0.59, 0.11)), texcol.a); texcol = texcol * i.color; return texcol; }; ENDCG } } FallBack Off } Enjoy!
It works for me when i build a windows version. But when i build a webGL version i get a bug. Im using it on a UI image. as i said it works in windows version In WebGL if i go fullscreen when the unity logo is showing all is fine. When i go fullscreen when the game is playing then any UI/Image with the material on it disappears. Works fine on Spriterenderer.