Trying to have sprites affected by fog without having to use default/diffuse shader.

The game I’m making uses a bunch of mobile/unlit textures on quads for walls, and this works well with fog. Walls fade away/appear as the camera moves away/towards them.

However when it comes to sprites, they are bright and unaffected by fog no matter the distance from them. I tried using the diffuse shader, but since my scene is completely unlit and I do not want to use lighting in general, I end up with really dark sprites. These dark sprites are affected by the fog, but they are too dark and the sprite only renders on one side.

Is there any way to have sprites affected by fog just like how mobile/unlit quads are? I did a lot of searching relating to this issue and found that sprites are technically quads, but I find that quads are behaving as desired with regards to fog while sprites are not.

I have tried using the mobile/unlit shader on a material I created and assigned to sprites, but this caused all the transparent parts of the sprite to become full of artifacts and stretched out textures for some reason.

Any help would be greatly appreciated.

Although the post is from some time ago, I just had the same small problem and I want to provide the solution I found, thanks to the fact that someone charitable shared a shader just for this in the following link:

https://www.reddit.com/r/Unity3D/comments/6scjlo/a_friend_of_mine_made_a_sprite_shader_which/

The shader what Im talking about is the next: // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

Shader "Sprites/Default with Fog"
 {
     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
     }
 
     SubShader
     {
         Tags
         { 
             "Queue"="Transparent" 
             "IgnoreProjector"="True" 
             "RenderType"="Transparent" 
             "PreviewType"="Plane"
             "CanUseSpriteAtlas"="True"
         }
 
         Cull Off
         Lighting Off
         ZWrite Off
         Blend One OneMinusSrcAlpha
 
         Pass
         {
         CGPROGRAM
             #pragma vertex SpriteVertFog
             #pragma fragment SpriteFragFog
             #pragma target 2.0
             #pragma multi_compile_instancing
             #pragma multi_compile _ PIXELSNAP_ON
             #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
             #pragma multi_compile_fog
             #include "UnitySprites.cginc"
 
             struct v2f_fog
             {
                 float4 vertex   : SV_POSITION;
                 fixed4 color    : COLOR;
                 float2 texcoord : TEXCOORD0;
                 UNITY_FOG_COORDS(1)
                 UNITY_VERTEX_OUTPUT_STEREO
             };
 
             v2f_fog SpriteVertFog(appdata_t IN)
             {
                 v2f_fog OUT;
 
                 UNITY_SETUP_INSTANCE_ID (IN);
                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
 
             #ifdef UNITY_INSTANCING_ENABLED
                 IN.vertex.xy *= _Flip.xy;
             #endif
 
                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
                 OUT.texcoord = IN.texcoord;
                 OUT.color = IN.color * _Color * _RendererColor;
 
             #ifdef PIXELSNAP_ON
                 OUT.vertex = UnityPixelSnap (OUT.vertex);
             #endif
 
 
                 UNITY_TRANSFER_FOG(OUT, OUT.vertex);
 
                 return OUT;
             }
 
 
             fixed4 SpriteFragFog(v2f_fog IN) : SV_Target
             {
                 fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
 
                 // apply fog
                 UNITY_APPLY_FOG(IN.fogCoord, c);
                 c.rgb *= c.a;
 
                 return c;
             }
 
         ENDCG
         }
     }
 }

It was enough for me to create a material, assign it to this shader and then assign it to my sprites that material.

So I’ve managed to solve the problem by modifying the default sprite shader to accept fog. I didn’t really know exactly what I was doing as I’m not familiar with how to program shaders, but I just took two partial solutions and glued them together to one full solution using the default unity shaders’ code (downloaded off this site).

Step 1: Create a new shader in your assets folder. I called mine “SpritesDefaultModded” and filed it under “Custom”. This is the code used for the shader (a combination of the default sprite shader and the mobile unlit shader aka mobile unlit-alpha shader):

Shader "Custom/SpritesDefaultModded"
{
	Properties
	{
		[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
		_Color ("Tint", Color) = (1,1,1,1)
		[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
	}

	SubShader
	{

		Tags
		{ 
			"Queue"="Transparent" 
			"IgnoreProjector"="True" 
			"RenderType"="Transparent" 
			"PreviewType"="Plane"
			"CanUseSpriteAtlas"="True"
		}

		Cull Off
		Lighting Off
		ZWrite Off
		Blend One OneMinusSrcAlpha

		Pass
		{
		CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#pragma multi_compile _ PIXELSNAP_ON
			#pragma multi_compile _ ETC1_EXTERNAL_ALPHA
			#pragma multi_compile_fog
			#include "UnityCG.cginc"

			struct appdata_t
			{
				float4 vertex   : POSITION;
				float4 color    : COLOR;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex   : SV_POSITION;
				fixed4 color    : COLOR;
				float2 texcoord  : TEXCOORD0;
				UNITY_FOG_COORDS(1)
			};
			
			fixed4 _Color;

			v2f vert(appdata_t IN)
			{
				v2f OUT;
				OUT.vertex = UnityObjectToClipPos(IN.vertex);
				OUT.texcoord = IN.texcoord;
				OUT.color = IN.color * _Color;
				#ifdef PIXELSNAP_ON
				OUT.vertex = UnityPixelSnap (OUT.vertex);
				#endif
				UNITY_TRANSFER_FOG(OUT,OUT.vertex);

				return OUT;
			}

			sampler2D _MainTex;
			sampler2D _AlphaTex;

			fixed4 SampleSpriteTexture (float2 uv)
			{
				fixed4 color = tex2D (_MainTex, uv);

				#if ETC1_EXTERNAL_ALPHA
				// get the color from an external texture (usecase: Alpha support for ETC1 on android)
					color.a = tex2D (_AlphaTex, uv).r;
				#endif //ETC1_EXTERNAL_ALPHA

				return color;
			}

			fixed4 frag(v2f IN) : SV_Target
			{
				fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
				c.rgb *= c.a;
				UNITY_APPLY_FOG(IN.fogCoord, c);
				return c;
			}
		ENDCG
		}
	}
}

Step 2: Create a new material, I called mine simply “SpritesMaterial”. Set the new material’s shader to the “SpritesDefaultModded” shader we just created. Make sure the tint of this new material’s alpha value is not 0, otherwise you will find your sprite is completely transparent/invisible. For some reason mine defaulted to that value.

Step3: In your sprite renderer, set the material to the “SpritesMaterial” we just created.

This should solve the problem. The result I got was:

  • Sprite is now affected by fog and
    darkens/lightens along with
    everything else in the scene despite
    being unlit.
  • Sprite renders on both sides (other
    shaders seem to make sprites’ back
    sides disappear).
  • All transparent parts of the sprite
    are intact, no artifacts or
    corruption to be seen.

You can set the global lightning of the scene in the Window panel ->lightning->scene.
That should do the trick !