Search Unity

Fog of War - Whats the best way

Discussion in 'General Graphics' started by Cafn, Jan 20, 2016.

  1. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19
    Hi there.

    Sorry if this is in the wrong place, but my first post in here.

    I'm implementing a RTS game and got the prototype almost completed. Right now only flocking movement and AI algorithms are missing. And the fog of war...

    I've started to test out some implementations that i've seen on the forums (sadly many have the links broken) but I find many people discussing different ways of how to implement the fog of war. Followed 1 tutorial which is for unity4, and other on youtube people say it will make the game slow if user has many units.

    What i've programmed and tested is having a plane (black for example) and change the alpha value of a several points inside a circle.Though, either i'm doing something wrong, but my fps dropped to 6, and it was a small circle with 5 pixel radius. Not sure if I shall change the colors in update method, but to me it makes sense that it's done in there.

    I've never worked with shaders (not saying that I never will, really want to learn more about that) but before I start something else, I really wanted to know what is the best way to implement it. The RTS game might have more then 500 units at the same time (note that its for 10 players online, and each one should have around 50 units +/-)

    Thanks for your time,
     
  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    That's the most common way...
    And that's not normal. It's not the fastest way of doing fog of war, that's true, but it shouldn't be that slow.
    Most of fog of war processing is done when computing where unit sees or where he doesn't. 50 units shouldn't lag at all. 500 might, depending on how you make it.

    Wait, you have... Plane? Not Quad? Why?
    And it brings me to... You didn't say the important thing: how exactly did you try rendering fog of war? I'd go with single quad with black texture and set texture's alpha to what is needed for all units then applying it one time per frame at maximum. Preparing values to paint on texture can be threaded(in case of many units), loading texture - coroutined for performance increase. Also, try to not .Apply() texture when possible - that's the most expensive operation, so if player doesn't see this part of fog(in case one quad with fog is not enough for quality you seek), don't update it for him.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    As long as you don't need line of sight (which makes things insanely expensive) the "best" way is probably using a second camera with a rendertexture to render circles into using a BlendOp Max shader. Then use the rendertexture as the texture on that quad.

    If you're doing fog of war by manipulating pixels from C# or javascript it's going to be slow.
     
    jopereira, sean244 and Harinezumi like this.
  4. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19

    Thanks you both for the answers. My problem was that I was using Apply every Update() call. changed it to be only each 10 frames, and it increased a lot. Not sure if thats the most correct way of doing it.

    I'm using a plane because was following a tutorial that used it. Shall it be better if I change to a quad?

    Other thing that you guys might have an idea of how doing it:

    I want my fog of war to cover all units, but not the terrain.(similiar to starcraft 2 online games) How can I implement this? Tried several stuff, but no luck
     
  5. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Plane is a tiny overdraw over Quad. It's too small gain to change now anyway. Just note that next time you choose which prefab to instantiate;)
    To cover units you first draw terrain, then you draw fog with depth write On then you draw units with depth test on. In that order. It will work assuming fog is always above units. That way is simple, however you might have problems if you have transparent things on units. If you understand how shaders work, you can play with depth a little more making fog being above units not necessary(but that will almost guarantee broken transparency).

    A tutorial on Texture2D way. In my experiment I could support 50 casters normally. Actually, it scales really well for large amount of units: the slowest part is Apply();. You can pretty much use a coroutine with texture.Apply(); to update when you can. And yes, I couldn't render it 60 times per second, unfortunately.

    I want to add that bgolus solution will indeed be faster, but requires you to understand shaders and be able to fix shaders yourself at least(just enough to fix bugs). If you need fog of war that updates itself each frame, that's the way to go. This one is a good simple tutorial. It will have problems with slow fading out of fog of war though and there you'll need to invent.

    There's even faster solution using stencil buffer, but that requires extensive shader programming, so it's just not viable as development time consumed. And I didn't find proper tutorial for that. But this way you can actually can write to stencil buffer when rendering fog and read from it when rendering units and as consequence you can avoid problems with transparency.

    EDIT: if you're making warcraft3-like fog of war, you paint fog of war on top of terrain texture (not above units) and enable/disable mesh renderers on units manually by checking whether they are seen or not. Like this. If you don't like them 'appearing' you can 'fade them in/out'. They did appear instantly in warcraft 3 as far as I remember.
    EDIT2: added links.
    EDIT3: finally found all links I wanted.
     
    Last edited: Jan 22, 2016
  6. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19
    Once again, really thanks for the help ;) I've tryed to follow those 2 tutorials, first one couldn't make it to work, sadly :/ And the second one sadly isn't much a tutorial, as there is almost to talking, lots of code, and I kinda got lost in there.

    To try to cover units, i've tryed to make the following with cameras, but not sure if I can make it this way or need anything else.

    Create 3 cameras:
    - 1st one to be renderer covers everything except fog of war and units
    - 2nd one covers fog of war
    - 3rd one covers units.

    But didn't work. I must work with shaders, right?

    Thanks once again for all the help
     
  7. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Shaders would speed performance up a bit, but not necessary. You didn't say what exactly the problem, so I'd try to guess:
    1. Check camera Depth in inspector - that's render order for cameras. If must be higher on fog and even higher on units.
    2. Check camera Clear Flags in inspector. Your fog and unit cameras should not clear anything. Your terrain(covers, well, whatever) camera should clear everything.
    3. Make sure your fog shader has depthwrite ("ZWrite On" line, it's On by default so just check there's no "ZWrite Off")
    If my guesses are wrong, tell me what's exactly wrong then.

    Um, yeah, just checked and it doesn't work in Unity5. I'll see if I understand why it doesn't blend properly.
     
  8. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19

    I've tryed with shaders, but i have no clue of what to put in there :/ Cameras i know a little bit more.

    About the cameras, here are some prints and here is what happens:

    Main Camera(Renders all except the Fog of War and units (later will be also buildings)):



    Fog of War Camera


    Units Camera


    And here is the code of the shader. I've copied from another place, I'm kinda lost with it (really got to spend some hours reading more about this)

    With a Standard shader, here's what happens:



    With the following shader (code comes after, here it is what happens)




    Shader "Custom/FogOfWarCG" {
    Properties{
    _MainTex("Base (RGB)", 2D) = "black" {}
    }
    SubShader{
    Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    Cull Off
    LOD 200
    Lighting off
    ZWrite On

    pass {
    CGPROGRAM
    #pragma vertex vert_img
    #pragma fragment frag

    #include "UnityCG.cginc"

    uniform sampler2D _MainTex;

    fixed4 frag(v2f_img i) : SV_Target{
    return tex2D(_MainTex, i.uv);
    }
    ENDCG
    }
    }
    FallBack "Diffuse"
    }

    Thanks a lot for the help mate.
     
  9. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Um, yeah, after fixing code from second tutorial I see where I was wrong. Texture needs not only ZWrite everywhere, but also don't ZWrite where we must see another unit. I'm posting unitypackage for it that works in 5.3.1f1.

    Dropbox link for fixed code

    Note: this code originally isn't mine, I've only fixed it. Originally it's posted here(same as link above for simple tutorial) (it didn't include culling before though).
    What I've fixed:
    1. MeshCombineUtility and some code that used it as described in this post
    2. Set third camera for units.
    3. Layers work: Move all units to Units layer. Move all ApertureMask objects to FogOfWar layer. Move FogOfWarPlane object to FogOfWar_Inside (yes, I meant the visa versa, but, well, it ended like that...) layer. Set FogOfWarPlaneCamera culling layer to FogOfWar_Inside layer. Set FogOfWarCamera culling to FogOfWar layer.
    4. Replace FogOfWarMask.shader contents with
      Code (CSharp):
      1. Shader "Custom/FogOfWarMask" {
      2.     Properties {
      3.         _Color("Main Color", Color) = (1,1,1,1)
      4.         _MainTex ("Base (RGB)", 2D) = "white" {}
      5.         _BlurPower("BlurPower", float) = 0.002
      6.         _Cutoff("Cutoff", float) = 0.01
      7.     }
      8.     SubShader {
      9.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
      10.         Blend SrcAlpha OneMinusSrcAlpha
      11.         ZTest Off
      12.         Lighting Off
      13.  
      14.         LOD 200
      15.        
      16.         pass {
      17.             CGPROGRAM
      18.             #pragma vertex vert_img
      19.             #pragma fragment frag
      20.  
      21.             #include "UnityCG.cginc"
      22.  
      23.             fixed4 _Color;
      24.             float _Cutoff;
      25.             float _BlurPower;
      26.             uniform sampler2D _MainTex;
      27.  
      28.             fixed4 frag(v2f_img i) : SV_Target{
      29.                 half4 baseColor1 = tex2D(_MainTex, i.uv + float2(-_BlurPower, 0));
      30.                 half4 baseColor2 = tex2D (_MainTex, i.uv + float2(0, -_BlurPower));
      31.                 half4 baseColor3 = tex2D (_MainTex, i.uv + float2(_BlurPower, 0));
      32.                 half4 baseColor4 = tex2D (_MainTex, i.uv + float2(0, _BlurPower));
      33.                 half4 baseColor = 0.25 * (baseColor1 + baseColor2 + baseColor3 + baseColor4);
      34.  
      35.                 float alpha = _Color.a - baseColor.g;
      36.                 clip(alpha-_Cutoff);
      37.                 return float4(_Color.rbg*baseColor.b, alpha);
      38.             }
      39.             ENDCG
      40.         }
      41.     }
      42.     Fallback "Diffuse"
      43. }
      Main reason for this is that surface blending was changed a lot and was broken in original shader. Couldn't bring it back to life using surface shader. Also needed clip(...) for disabling ZWrite where we see objects. Might not be the most elegant or beautifully looking solution, but, hey, it works! If you don't need clipping of units like in tutorial you can remove units camera, clip(...) statement and set ZWrite Off for that shader statement (not what OP wants though)..
    EDIT: Fixed issue in code posted here with very high terrain is not covered with fog by adding ZTest Off. Issue is present in unitypackage I linked above.
     
    Last edited: Jan 26, 2016
  10. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19
    Thanks a lot for the help. I've got it working really nice, and with way more fps then I had before (almost the double)

    Only one final thing left. When a unit moves, the fog disapears on the path the unit walked. Though I want the path to reset to the original position when there's no unit there.

    Thanks once more
     
  11. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Um... I'm still not sure where issue in that example is. Cameras seem to be set up properly.
    Some more guesses:
    1. Is fog of war higher than units by your UP vector? (Closer to camera).
    2. What shader do your units use? (needs to have ZTest On - it's On by default but Off for most shaders with transparency). Standard shader usually has in On.
    Oh, and I've finally remembered a major problem with rendering units and terrain in different cameras: there are no shadows between different cameras. Something needs to be fixed for it to work.
    P.S. If you make your all units disappear, that's half success already.

    EDIT: you posted while I was still writing. And I'm hesitant to remove what I wrote already... Here's answer to
    Set FogOfWarCamera clear flags to black color (requires editing or removing Auto Clear RT script on it as it will reset that value) and change blob texture in FogOfWar folder to have better values (right now if you check "Alpha is transparency" on it and apply you will see that alpha actually is linear while it needs to have large area of 255 alpha(white) in middle with maybe falloff at edges for that to look good).

    One of solutions for shadows I know:

    it's not best performance-wise, but performance should only be issue if after testing it won't give needed result.
     
    Last edited: Jan 26, 2016
  12. Cafn

    Cafn

    Joined:
    Jan 8, 2015
    Posts:
    19

    Got it working smoothly ;) Thanks a lot for the help, all problems seem to be solved.

    About the shadows, will see that tutorial and follow it ;) Thanks once more