Tri-state blended fog of war

SpacedCowboySpacedCowboy Posts: 1Member
in 2D

Hi :)

Newbie here. I've been (up until now) coding in SDL and have been thinking about porting to Godot, since I'm very interested in using compute shaders cross-platform in 4.0 ...

I've been getting my head around nodes etc, but I'm having some difficulty trying to figure out the render-to-texture story. The prime reason I'm interested is the fog-of-war in the post-title. The effect I'm after is basically what I already have in SDL ...

... where there are 3 areas: black (unexplored), darkened (been there, but it's currently out of range to see) and lit (what I can see right now). There's an intensity ramp between all of these, they're not boolean states.

In SDL I do:

  • Render screen-sized chunk of [map] to frame buffer {BLENDMODE_NONE}

Start off by copying the screen-sized map to the screen. This is what will "show through" the fog

  • Render screen-sized chunk of [fog-of-war] to [lights] {BLENDMODE_NONE}

Copy the same location/area of the fog-of-war texture to a temporary working space

  • Render [maxed-fog] into [lights] for each player {BLEND_MAX}

The maxed-fog is a gaussian blur that would fill the 800x800 pixel texture, max intensity at the centre, rendered as if it were 0..255 for intensity but any pixel over intensity 96 is capped at 96. Think of it like "chopping off" the top of the 2D gaussian curve, it's table-top mountain. It's important that this is done for all players before we...

  • Render screen-sized [lights] to [fog-of-war] {BLENDMODE_NONE}

Now that we have what we want to store in the fog-of-war (to provide the 'seen but not currently visible' effect for the current state), copy that entire RGBA back into the fog of war. This is wasteful of texture space for what is a single intensity channel, but SDL only uses 32-bit textures as far as I can tell.

  • Render [full-range-fog] into [lights] for each player {BLEND_MAX}

Now overwrite the temporary buffer (which will never be persisted, just shown for this frame) with real 0..255 gaussian lights. That way we get the "this is what you can see right now" effect just around the player characters.

  • Render [lights] to frame buffer {BLENDMODE_MOD}

Finally, overwrite the screen (which currently holds the map at full brightness) with the temporary buffer, where src and dst RGB are multiplied, thus making only the lit areas visible.

In the above:
[map] is the huge map-area (max is probably 8k x 8k pixels)
[fog-of-war] is the same-sized overlay which masks out non-visited areas
[full-scale-fog] is an 800x800 pixel 2D-gaussian-like profile, values from 0…255
[maxed-fog] is the same but has its values capped to 96
[lights] is a screen-sized buffer for temporary work

BLENDMODE_NONE is a straight copy (dst RGBA -> src RGBA)
BLENDMODE_MOD is (dst RGB -> src RGB * dst RGB, A unchanged)
BLEND_MAX has dst RGBA being the maximum of src and dst

The repeat render for [maxed-fog] (which is then copied into the overall fog-of-war) and [full-range-fog] (copied onto the framebuffer but not persisted) is what gives you the tri-state fog, with the nice tail-off into invisibility.

Now this probably isn't the best way to do it if shaders are available - I'm just using the standard SDL Render* calls - but I'm having some difficulty trying to see how you'd set up the node hierarchy to make this work.

Would it be possible to set up a shader to do all the above ? Any hints, if so ? Do you set up a single shader with inputs for textures (map, fog-of-war, maxed-fog, full-scale-fog, and all the player co-ords and just let it run through ?)

Or am I better off duplicating the same sort of workflow with Scripts rendering to a viewport, and effectively doing just the above ?

Any ideas gratefully received :) What I'm trying to do is figure out if I can make the leap to the game engine rather than continue with SDL, and have a reasonable chance of getting as far as I've got with SDL :)

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file