+3 votes

I started developing a 3D RTS game in Godot, and I wanted to implement FOW, but I didn't find any suitable tutorials (actually none at all) to do that.

As I am not very familiar with shaders (or Godot), I would like to know what is the best practice to implement it.

I have a terrain and some units on it, each unit has its field of view (range) made from area and inherited collision shape. I already set the enemy units to visible if they enter the area.

I would like to implement three state Field of view (like age of empires) for the terrain:

  1. state - black, undiscovered place
  2. state - normal (everything visible),
  3. state - terrain visible, but with a little bit of fog or a little bit darker than normal state (discovered but no units currently present)

What is the best way to achieve this?

asked Jun 25, 2019 in Engine by gmaps (692 points)

1 Answer

+2 votes

Since noone answered, I'll answer myself as I have successfully implemented a FOW (with some issues though).

So first, step, we need a texture. I usually use a texture of map_size * 2.

Next, we need to paint the areas where the units are. I tried doing that in GDScript, i hade premade matrices and just pasted them to an image i then converted to texture. Even though its O(n) performance, It had a big impact on FPS. I was advised on Godot discord to do the brute force GPU implementation. I encoded the unit positions and ranges into texture and then looped over them in the shader. If it gets slow with unit number I can always implement some version of quadtree to speed it up. If a location is in range of a unit, I make the color 1 (Red channel), otherwise make it 0 (or in between if it's on the edge - maybe this fade step is unnecessary as I also do a blurring step aftewrards).

But it's also important to check the previous value as we also want to store information about the discovered area. So when I set Red channel to 1, i also set the Green channel to max(0.5 * 1.0 (value of red), previous_green). And in next iteration before processing the locations I retrieve the pixel screen texture and set the current red color to previous green.

I added a timer to 0.1s to compute the texture, which was enough for "smoothy" look.

So the areas that I currently see have orangy color (combination of red and green), the discovered ones are green and others are black.

Then I do two pass blur on the texture.

Second step:

I add a quad (check this article) to screen that represents the Fog of war, and give it the visibility texture to sample. I get the pixel location, and check if it's level of visibility and I turn alpha on the quad to "visibility" level.

And that's it.

Note: the transparent objects will have issues with this method and will have to be "fog shaded" separately, by passing visibility texture directly to their shader.

Note2: Downside of this approach is that if you move camera in nondiscovered area and look against the discovered, you may see black silhouettes of objects in nondiscovered area (e.g. trees). From gameplay perspective it's not that bad, as we can hide and show units based on physics areas, so we can't use this hack to see units/buildings. Fixing it may include passing the visibility shader to all objects and then moving all y coordinates to 0 in vertex shader. But it's not worth the hassle, at least for me. If anyone has an idea how to solve this issue in an convenient manner, please do tell.

Feel free to comment if you have questions/issues with FOW.

answered Oct 7, 2019 by gmaps (692 points)
edited Oct 8, 2019 by gmaps
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.