Writing Fog of War Beacons to the Material in Unreal Engine

Add stationary vision sources to top-down fog of war in Unreal Engine. The Fog Manager packs each beacon's location, radius, and FOV into FVector4 arrays and hands them to a Niagara system, and the material unions the beacon mask with the player's reveal.

This is step six of the Top-Down Fog of War in Unreal Engine guide. The player reveal lights the area around the player. Beacons add extra, stationary vision sources: a watchtower, a ward, a deployed scanner that lights its surroundings even when the player is nowhere near. This step is how the manager gets them onto the GPU; the Niagara system that draws them is the next step.

Building the beacon arrays in C++

The manager keeps two parallel arrays of FVector4 and rebuilds them every tick: one with each beacon’s location and radius, one with its facing and FOV cosine.

void AFogManager::UpdateBeaconLocations()
{
    BeaconLocationsAndSizes.Empty();
    BeaconFOVData.Empty();
    if (!GetLocalTracker()) return;

    for (const UBeaconComponent* Beacon : GetLocalTracker()->GetTrackedBeacons())
    {
        if (!Beacon) continue;
        const FVector L = Beacon->GetComponentLocation();
        const FVector Offset = bIsMapCentered ? FVector(MapSize / 2, MapSize / 2, 0) : FVector::ZeroVector;
        // xy = location, z = height, w = radius
        BeaconLocationsAndSizes.Add(FVector4(L.X + Offset.X, L.Y + Offset.Y, L.Z, Beacon->VisibilityRadius));

        const FVector F = Beacon->GetOwner()->GetActorForwardVector();
        const float CosHalf = Beacon->FieldOfViewAngle < 360.f
            ? FMath::Cos(FMath::DegreesToRadians(Beacon->FieldOfViewAngle * 0.5f)) : -1.f;
        BeaconFOVData.Add(FVector4(F.X, F.Y, CosHalf, 0.f)); // xy = forward, z = cos(half FOV)
    }
}

Each beacon carries the same data the player does, a location, a radius, and a facing and FOV, just packed into vectors so a variable number of them can ride along together. The UBeaconComponent is the source of that data; the local tracker hands back every beacon the player owns through GetTrackedBeacons.

Handing the arrays to Niagara

A material cannot loop over a CPU array, so we hand those arrays to a Niagara system that draws every beacon’s spotlight into a beacon render target, and the material samples that target as Beacon Spotlights. In the Fog Manager Blueprint this is two Niagara Set Vector 4 Array calls each tick (User.BeaconLocationsAndSizes and User.BeaconFOVData), plus a one-time User.MapSize on BeginPlay so the system draws in the same UV space as everything else.

The NS_Beacon_Locations_To_RenderTarget Niagara system open in the Unreal editor, with the emitter that writes beacon spotlights into a render target the fog material samples

That is the whole handoff: rebuild the arrays in C++, push them onto the system as user parameters. How the system turns those arrays into a texture, the Grid2D and its simulation stages, is step seven.

Combining beacons with the player’s reveal

The material samples the beacon render target as another spotlight mask. Each vision source is intersected with the floor (min), and the sources are unioned together (max), so any of them can reveal a pixel:

float playerReveal = min(floorMask, playerSpotlight);
float beaconReveal = min(floorMask, beaconSpotlights);
float mask         = saturate(max(playerReveal, beaconReveal));

If your game has no beacons, drop this whole branch: the combine just uses the player reveal.

The fog material sampling the beacon render target as the Beacon Spotlights mask, ready to union with the player reveal

What’s next

The manager has handed the beacon arrays to the GPU. Next, look inside the system that turns them into a texture: Niagara and Grid2D setup. See the guide hub for the full series.

Frequently asked questions

How are beacons sent to the GPU?
The Fog Manager packs each beacon's location and radius into one FVector4 array and its facing and FOV cosine into another, then sets both on the Niagara system every tick with Niagara Set Vector 4 Array.
Why use Niagara instead of doing beacons in the material?
A material cannot loop over a variable-length array of beacons. A Niagara Grid2D system can: it iterates the beacon arrays on the GPU and rasterizes each one's spotlight into a render target the material then samples as a single mask.
How are beacon reveals combined with the player's vision?
With a max (logical OR): the final reveal is max(player reveal, beacon reveal), so any vision source can light a pixel.