Search Unity

Cheap way in making Fog of War

Discussion in 'Scripting' started by Rukas90, Oct 15, 2018.

  1. Rukas90

    Rukas90

    Joined:
    Sep 20, 2015
    Posts:
    169
    Hello,
    I'm working on an RTS game and I am trying to implement Fog of war. In my game there will be lots of units and buildings.

    I know that fog of war and easily become really heavy. Especially when there's lot's of units / building in the game. What is the cheapest way in making fog of war?
    I don't need anything necessarily fancy as well.

    I'm pretty sure that I need to write shaders to achieve that, the problem is that I am not so good with shaders. As a matter of fact I'm quite terrible..

    Can someone give some advice on this?
    Thank you
     
  2. Well, the simplest way to make a fog of war is to make a 3D volume above the ground. When you are marking a place to visible, you cut out another shape from this cuboid.
    And then you can use some opaque, but smoke-like texture and shader and it would look like clouds or smoke.
    Like in CIV5: https://external-preview.redd.it/w3...bp&s=03d2a3184e454967ae78888f7182a77f1771d72c

    + you don't need shaders really
    - you need to change mesh runtime, especially because you need to adjust the details (vertices) of the cuboid
     
    ArturDenislamov and jumisko like this.
  3. Rukas90

    Rukas90

    Joined:
    Sep 20, 2015
    Posts:
    169
    Thanks for the reply! That's an interesting solution and could definitely work for my game, well ish I was thinking more like a black fog, like in most RTS games, like WC3 for example. Still this could work, tho isn't mesh deformation every frame really expensive? Considering the amount of units and buildings in my game the fps would probably suicide.. But I don't know maybe I'm wrong.
     
  4. up to you :) -> shader

    Why would you do it every frame? You only deform the mesh when a friendly unit moves/changes view. (When the fog changes)
     
  5. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    You can use RenderTextures to produce Fog of War to great effect. Here's a few simple steps you can follow to quickly get a simple Fog of War set up.

    1) Create a Rendertexture in your project, name it whatever you like. Set the dimensions to 2D, size to 1024x1024 (or lower if needed), and color to ARGB32. You can tweak the other settings for quality but they don't matter too much. this texture will be used to map what areas of the map are revealed and which areas are shrouded in fog

    2) In your scene create a "Fog Camera" specifically for writing to the Fog texture. in most cases you'll want the camera to be center high above your map looking straight down and set to orthographic. Adjust the size so that it sees the same area that the fog would visibly cover. other important settings include setting the target texture to the new RenderTexture you created, and setting the culling mask to a specific layer that will have objects that clear the fog (I call this layer the Fog Stencil). Now from here on the other settings on the camera will dictate the type of Fog behavior you want:
    • Dynamic Fog: (To have the fog clear when you get near and recover when you leave, for showing current vision) Clear flags = Color, Background color =transparent black,Culling Mask = Fog Stencil, Target Texture = fog renderTexture
    • Static Fog: (To have the fog remain uncovered once explored, for showing explored areas) Clear Flags = Depth,Culling Mask = Fog Stencil, Target Texture = fog renderTexture
    • Disable Fog Removal: (previously explored areas remain cleared but walking into the darkness doesn't clear fog)Clear Flags = Depth, Culling Mask = nothing, Target Texture = null
    • Reveal All: (remove all fog from the map, like for cheats) Clear Flags = Color, Background = white,Culling Mask = Fog Stencil, Target Texture = fog renderTexture
    • Shroud All: (cover entire map in darkness, vision does not clear fog) Clear Flags = Color, Background = black, Culling Mask = Nothing

    3) Next lets add the actual Fog the players will see to the map. simply Create a Plane gameobject and have it scaled up so that it fits as close as possible to match the camera's visible bounds that you set earlier. As long as you are close enough the fog should appear to line up correctly when finished. Raise this plane so that its above your all the elements in your terrain but below your main camera so that it can properly obscure the map. Also just a note that you might need to spin the plane 180 degrees on the y-axis depending on your setup. To make the Fog render correctly we'll need to add a custom shader,

    Now for the Shader, you mentioned that you have no experience with shaders, no worries you can use the one provided below. Create a new Material and have it use this shader:
    Code (CSharp):
    1. Shader "Painted Render Texture Shader"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (0,0,0,0.7)
    6.         _MainTex ("Canvas", 2D) = "white" {}
    7.         _Smoothness ("Feather", Range(0,0.1)) = 0.005
    8.     }
    9.  
    10.     SubShader
    11.      {
    12.         Tags { "Queue"="Transparent" "RenderType"="Transparent" "LightMode"="ForwardBase" }
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         Lighting off
    15.         LOD 200
    16.      
    17.         CGPROGRAM
    18.         #pragma surface surf NoLighting noambient alpha:blend
    19.  
    20.         fixed4 _Color;
    21.         sampler2D _MainTex;
    22.         float _Smoothness;
    23.  
    24.         struct Input
    25.         {
    26.             float2 uv_MainTex;
    27.         };
    28.  
    29.         fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, float aten)
    30.         {
    31.             fixed4 color;
    32.             color.rgb = s.Albedo;
    33.             color.a = s.Alpha;
    34.             return color;
    35.         }
    36.  
    37.  
    38.         void surf (Input IN, inout SurfaceOutput o)
    39.         {
    40.  
    41.             half4 gaussianH   = tex2D (_MainTex, IN.uv_MainTex + float2(-_Smoothness,0))*0.25;
    42.             gaussianH  += tex2D (_MainTex, IN.uv_MainTex                         )*0.5  ;
    43.             gaussianH  += tex2D (_MainTex, IN.uv_MainTex + float2( _Smoothness,0))*0.25;
    44.  
    45.             half4 gaussianV   = tex2D (_MainTex, IN.uv_MainTex + float2(0,-_Smoothness))*0.25;
    46.             gaussianV  += tex2D (_MainTex, IN.uv_MainTex                        ) *0.5  ;
    47.             gaussianV  += tex2D (_MainTex, IN.uv_MainTex + float2(0, _Smoothness))*0.25;
    48.  
    49.             half4 blurred    = (gaussianH+ gaussianV)*0.5;
    50.  
    51.             o.Albedo = _Color.rgb * blurred.g;
    52.             o.Alpha = _Color.a - blurred.g;
    53.         }
    54.  
    55.         ENDCG
    56.     }
    57.     FallBack "Diffuse"
    58. }
    59.  
    Then drag your Fog RenderTexture onto the Canvas field for the shader, the other fields should be fine for most circumstances.

    Note: the Smoothness|Feather is not a requirement for a functional fog, and you'll likely want to keep the value small. It just adds a little quality to the fog, and can be safely removed if needed.
    Also note that the Shader only reads the green channel.

    4) Now its time to add the vision stencils to the entities in your map which will provide you vision and clear the fog. Select a gameobject that will be providing vision and add a child. This child will use the same Fog Stencil layer that the camera. Here you can use a Sprite Renderer, a Textured Plane, or even a procedural mesh (for line of sight), using a textured plane is the simplest option. The texture you likely want for is a feathered, semi-transparent circle. The feather will help the fog look better, and Unity's Default Particle Texture is a great starting example, but you'll likely want a tighter feather. For simplicity, the shader my stencil used also was simply Unity's Particles/Additive shader, I didn't need to write a new shader. You can also instead use a cone texture if you want a flashlight or field of vision effect.

    5) Simply having the rest of the map be semi-dark yet still have all the enemy units visible in the darkness kind of defeats the purpose of the Fog so you need to be able to turn off the entities you want to be obscured by the fog. You can attach this script to those entities:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. //turns off the referenced renderer if the fog camera sees the transform's position on a "fog" pixel
    4. public class TextureStencilScript : MonoBehaviour
    5. {
    6.     public Camera cam; //The Camera using the masked render texture
    7.     public Renderer myRenderer; // reference to the render you want toggled based on the position of this transform
    8.     [Range(0f,1f)]public float threshold = 0.1f; //the threshold for when this script considers myRenderer should render
    9.  
    10.     // made so all instances share the same texture, reducing texture reads
    11.     private static Texture2D myT2D;
    12.     private static Rect r_rect;
    13.     private static bool isDirty = true;// used so that only one instance will update the RenderTexture per frame
    14.  
    15.     private Color GetColorAtPosition()
    16.     {
    17.         if(!cam)
    18.         {
    19.             // if no camera is referenced script assumes there no fog and will return white (which should show the entity)
    20.             return Color.white;
    21.         }
    22.  
    23.         RenderTexture renderTexture = cam.targetTexture;
    24.         if(!renderTexture)
    25.         {
    26.             //fallback to Camera's Color
    27.             return cam.backgroundColor;
    28.         }
    29.  
    30.         if(myT2D == null|| renderTexture.width != r_rect.width || renderTexture.height != r_rect.height)
    31.         {
    32.             r_rect = new Rect(0,0,renderTexture.width,renderTexture.height);
    33.             myT2D = new Texture2D((int)r_rect.width,(int)r_rect.height,TextureFormat.RGB24,false);
    34.         }
    35.  
    36.         if(isDirty)
    37.         {
    38.             RenderTexture.active = renderTexture;
    39.             myT2D.ReadPixels(r_rect,0,0);
    40.             RenderTexture.active = null;
    41.             isDirty = false;
    42.         }
    43.  
    44.         var pixel = cam.WorldToScreenPoint(transform.position);
    45.         return myT2D.GetPixel((int)pixel.x,(int)pixel.y);
    46.     }
    47.  
    48.     private void Update()
    49.     {
    50.         isDirty = true;
    51.     }
    52.  
    53.     void LateUpdate()
    54.     {
    55.         if(!myRenderer)
    56.         {
    57.             this.enabled = false;
    58.             return;
    59.         }
    60.  
    61.         myRenderer.enabled = GetColorAtPosition().grayscale >= threshold;
    62.     }
    63. }

    Hook up the fields on the script and then hit play, and you should have a working fog of war system. You can even increase the threshold for some entities, effectively giving a "stealth mechanic" so that they would only be visible to entities with "brighter" vision stencils. You can even create a 2nd RenderTexture and Fog Camera so that you can layer the fog effects if you want(like having both Dynamic and static fog to mix current vision and explored areas).
     
    PowerJake, Myrthrost, mikest and 22 others like this.
  6. Atix07

    Atix07

    Joined:
    Jul 27, 2013
    Posts:
    182
    Maybe mask will work?

    İts just an idea. Im making a 2D platformer and when I enter the caves, I use Sprite mask to hide far side.
    Başlıksız-1.jpg
     
    ArturDenislamov likes this.
  7. Rukas90

    Rukas90

    Joined:
    Sep 20, 2015
    Posts:
    169
    Thanks for reply! :) This could definitely work, but only for 2D game I believe. My game is in 3D so I'm pretty sure is will not work for me.
     
  8. Rukas90

    Rukas90

    Joined:
    Sep 20, 2015
    Posts:
    169
    Wow thanks for such a detail answer! I'll try to make this work and let you know if I did. :)
     
  9. Suduckgames

    Suduckgames

    Joined:
    Nov 28, 2016
    Posts:
    218
    This is INCREDIBLE what a perfect guide! A big thank you very much for such a detailed guide!

    I have two questions to you if you don't mind, I am unable to make a soft edge which parameters I need to tweak in order to make it a softer edge? I have tried changing the texture to something more smooth and changing scaling of render texture but unable to make something smooth with a render texture of 2048x 2048

    Also I am unable to make the shape of the unit to match the fog plane to have constant visibility, I think that is due to the size of the camera with the size of the camera projection, since I will have a dynamic map size, do you have any input about how I can handle this?

    Anyway, thank you very much for such a detailed guide!
     
  10. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    I usually make the stencil texture itself be a soft blob and just increase the softness value on the shader material.

    I'm unsure exactly what you mean about the shape of the unit matching the fog. This fog system is just a simple quick starter, If you want something to be more accurate from different perspectives opposed to the Fog camera, you'll have to think up a different design to solve that
     
  11. e01100111e

    e01100111e

    Joined:
    Aug 5, 2017
    Posts:
    20
    Awesome guide! However I cant paint the fog texture with the stencil :( Given shader is not compatible with URP so I used another shader. I can see it like this but it just changes position instead of removing the fog on the texture.

    upload_2021-10-3_13-22-44.png
     
  12. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    Sorry I've been busy at work all week. I've been meaning to do an updated post using URP. I'll see if I can get one out over the weekend.
     
  13. Nurallaerel

    Nurallaerel

    Joined:
    May 6, 2015
    Posts:
    13
    Heya, if you posted the update version for URP please provide the link :) If not, I guess I'm just bumping this
     
    icosajim likes this.
  14. jmattarock_unity

    jmattarock_unity

    Joined:
    Mar 10, 2018
    Posts:
    25
    Any update on the URP version?