© Oliver Keppelmüller 2017-2018
Developer’s blog
How not to be seen 25th February 2018 by Oliver Keppelmüller
WHAT ARE YOU HIDING? The difficulty of creating a fog of war (FOW) engine from a technical perspective is mainly performance. To understand the problematic I will try to explain the different steps of the engine. First we need to determine the line of sight (LOS) which is quite performance intensive: On the one hand the calculation needs to be done for every unit, on the other hand the calculation itself is very complicated. Here, we need to do a so called "raycast" into every direction (this means 360°) of the unit. Raycast means: we send out a ray towards a specific direction and check if it "hits" something, in our case mostly terrain or buildings. If a hit occurs, the line of sight stops there and we have the distance of the line of sight for this specific direction. If we assume a total range of 360°, this means we would have to calculate 360 raycasts which would be a lot when doing this for 200 units, and this in real- time! To reduce the amount of calculations, we can reduce the field resolution of our “FOW matrix” and thus the steps in turning around. For example: if you have only 33 fields in the scope of your unit, then you can only see, or not see, 33 angles instead of 360. In general, the FOW does not need to have that high resolution than the basic battlefield as it will nevertheless get blurred afterwards. The next problem we stumble upon is the terrain slope, especially as we don’t use standard square- or hex-based, simplified terrain. If you imagine we always look straight ahead, we may stop our line of sight at smaller obstacles (eg. hills) on the way, although there may be higher ground in a greater distance which could be seen. To overcome this we need again a lot more calculations. We now do not only "look" at the same elevation of the unit but on the target elevation of every field in front of the unit, meaning we check every field in front of the unit with the target terrain height as seen in these two images: With this modification we may have situations where closer terrain is invisible, while terrain farther away is visible again because it is on higher ground. We also use this logic for objects in way, like buildings, forest and even corn fields. Although we get quite an accurate FOW now, we lose performance (again). If we use the previous example, we would now have 200 units with 33 raycast angles each and maybe 40 fields in front, bringing it to a total of 264.000 calculations (way too much!). To reduce this, we can take into account, that raycasting shorter ranges does not need that many angles like ranges with greater distances, as the radius projected on the FOW field matrix is much less. The following image describes this: The blue distance (far) shows we need 6 angles to "fill up" (check) every FOW field, the green distance (near) shows we need only 2 angles because the radius is much smaller. So we can reduce the calculations to approx. the half of the 264k calculations with this step. As this is still too much for a real-time calculation, we need to spread all these calculations over multiple cycles, eg. you do not calculate 200 units per frame but only 5. The pro is a huge performance pickup, the con a lag in FOW update. This is also mainly the reason why the FOW in the most complex games is changing slowly. As we now have a tremendous performance win, we can spend a bit of it for visuals. All the calculations we have made result in a simple "visible" or "not visible" marking for each field of the FOW matrix. We now paint all the not visible fields as black pixels on a texture (with maybe some alpha). This texture is blended over the terrain which results in darker areas (not visible) and lighter ones (visible). But if we take a look on it, it shows up quite blocky as we only use total black or invisible pixels. To get rid of this, we need to blur the texture, or at least the values that lead to the texture (which should be faster). Using a full Gaussian blur costs tremendous performance (again). After a few tries I managed to implement simplified Gaussian blur with performance improved from 50ms per calculation to approx. 1ms by only having blur errors of <1% of the pixels. Image: Taking the theory to the battlefield, here’s the same in practice. Terrain visible for the selected army is shown lighter, while unseen terrain is colored darker. In this debug- image, the red areas mark terrain that is visible, but could conceal enemy troops (within the sparse woods). NOTE: the image is pre-alpha, and all visuals are subject to change. If you have survived my technical torture this far, a few lighter topics for the end: We have also implemented the reduction of the line of sight due to smoke from weapons and also taking into account different weather situations. But there is another goodie: concealment. Even if enemy units are within visible FOW ranges, they may still be concealed because of terrain (eg. placed in a corn field), cover (eg. hidden behind a stone wall or in a trench), laying down, or simply the small size, or skill in use of terrain, of the unit (eg. scouts, skirmishers). Oliver
Fog of war. Not the lack of clarity described by von Clausewitz, but the visual representation of what your units can see, as it’s commonly called in games. In Grand Tactician’s huge real-time battles visibility in combat requires quite complex calculations, as we take into account line of sight, weather, terrain concealment, and even smoke from weapons. Oliver surfaced from the matrix momentarily to explain more, from the technical point of view.