# RTS: Fog of War

**URL:** <https://forum.babylonjs.com/t/rts-fog-of-war/35479>\
**Category:** Questions\
**Created:** [November 7, 2022, 7:51pm UTC](https://forum.babylonjs.com/t/rts-fog-of-war/35479 "2022-11-07T19:51:11Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![sebavan](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/sebavan/32/57_2.png) [@sebavan](https://forum.babylonjs.com/u/sebavan)\
**Post date:** [November 10, 2022, 9:18pm UTC](https://forum.babylonjs.com/t/rts-fog-of-war/35479/21 "2022-11-10T21:18:15Z")

</div>

Yes Yes Yes and Yes 😉

---

<div class="post-metadata">

**Author:** ![Takemura](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/takemura/32/25836_2.png) [@Takemura](https://forum.babylonjs.com/u/Takemura)\
**Post date:** [November 12, 2022, 10:26pm UTC](https://forum.babylonjs.com/t/rts-fog-of-war/35479/22 "2022-11-12T22:26:30Z")

</div>

> [@carolhmj](#):
>
> Can you try checking if this one works: [Simple Fog of War | Babylon.js Playground (babylonjs.com)](https://playground.babylonjs.com/#8WJTJG#9)?

I can confirm, it is working greatly now!

> [@slin](#):
>
> Passing an vec3 array of unit positions is supported on WebGL2, but not WebGL1. Also if this array size is at scale of tens and hundreds, it adds overhead to loop through this array in fragment shader to check if fogOfWar or regular color should be applied. In addition to fog of war, you might also want to have custom fragment shader for highlighting a group of selected units. That will also add more overhead.

That seems I won’t be able to do this with shaders. Let’s see who else kick-in with their experience 🙂

---

<div class="post-metadata">

**Author:** ![Takemura](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/takemura/32/25836_2.png) [@Takemura](https://forum.babylonjs.com/u/Takemura)\
**Post date:** [July 11, 2023, 8:27pm UTC](https://forum.babylonjs.com/t/rts-fog-of-war/35479/23 "2023-07-11T20:27:44Z")

</div>

After getting more into Shaders, I was able to solve it:

> **[Babylon.js Playground](https://playground.babylonjs.com/#BRXZVE%238)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL 3D scenes

Fog of War Shader is for WebGL2 only.

Also I wanted to share my old Shader (only WebGL1) for i.e. RTS/RPG Entity Selection Circles:

> **[Babylon.js Playground](https://playground.babylonjs.com/#GCNNPT%2336)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL 3D scenes

---

<div class="post-metadata">

**Author:** ![JHines](https://avatars.discourse-cdn.com/v4/letter/j/67e7ee/32.png) [@JHines](https://forum.babylonjs.com/u/JHines)\
**Post date:** [November 16, 2023, 10:30pm UTC](https://forum.babylonjs.com/t/rts-fog-of-war/35479/24 "2023-11-16T22:30:53Z")

</div>

Just for anyone wanting to use a plugin instead I have made one here:

```typescript
type RevealerData = {
    position:Vector2;
    range:float;
}

export class FogOfWarPluginMaterial extends MaterialPluginBase {
    static maxRevealers = 1000;
    static revealerData:RevealerData[] = []; 
    static maxRevealerRange = 0;

    static resetRevealers() {
        this.revealerData = [];
        this.maxRevealerRange = 0;
    }

    static addRevealer(newRevealer:Vector2, range:number) {
        if(this.revealerData.length > this.maxRevealers) {
            console.error("Too many revealers added!");
        }
        if(range > this.maxRevealerRange) {
            this.maxRevealerRange = range;
        }
        this.revealerData.push({range:range,position:newRevealer});
    }

    static getRevealerData():{revealerPositions:number[],revealerRanges:number[]} {
        this.revealerData.sort((a, b) => a.position.x - b.position.x);
        const revealerPositions:number[] = new Array(FogOfWarPluginMaterial.maxRevealers*2).fill(0);;
        const revealerRanges:number[] = new Array(FogOfWarPluginMaterial.maxRevealers).fill(0);;
        for(var i = 0; i < this.revealerData.length;i++){
            const data = this.revealerData[i];
            const revealerId = i*2;
            revealerPositions[revealerId] = data.position.x;
            revealerPositions[revealerId + 1] = data.position.y;
            revealerRanges[i] = data.range;
        }
        return {
            revealerPositions:revealerPositions,
            revealerRanges:revealerRanges
        }
    }

    _isEnabled = false;

    constructor(material:Material) {
        // last parameter is a priority, which lets you define the order multiple plugins are run.
        super(material, "FogOfWar", 200, { "FogOfWar": false });
        this.isEnabled = true;
    }

    get isEnabled() {
        return this._isEnabled;
    }

    set isEnabled(enabled) {
        if (this._isEnabled === enabled) {
            return;
        }
        this._isEnabled = enabled;
        this.markAllDefinesAsDirty();
        this._enable(this._isEnabled);
    }
        
    prepareDefines(defines:any, scene:Scene, mesh:Mesh) {
        defines["FogOfWar"] = this._isEnabled;
    }

    getClassName() {
        return "FogOfWarPluginMaterial";
    }

    getUniforms() {
        return {
            "ubo": [
                { name: "revealers", size: 1, type: "float", arraySize: 2 * FogOfWarPluginMaterial.maxRevealers },
                { name: "ranges", size: 1, type: "float", arraySize: FogOfWarPluginMaterial.maxRevealers },
                { name: "maxRevealerRange", size: 1, type: "float" },
                { name: "revealersCount", size: 1, type: "highp int" }
            ],
            "fragment":
                `#ifdef FogOfWar
                    uniform float revealers[${FogOfWarPluginMaterial.maxRevealers}];
                    uniform float ranges[${FogOfWarPluginMaterial.maxRevealers}];
                    uniform highp int revealersCount;
                    uniform float maxRevealerRange;
                #endif`,
        };
    }

    bindForSubMesh(uniformBuffer:any, scene:any, engine:any, subMesh:any) {
        if (this._isEnabled) {
            const revealerData = FogOfWarPluginMaterial.getRevealerData();
            uniformBuffer.updateFloat("maxRevealerRange", FogOfWarPluginMaterial.maxRevealerRange);
            uniformBuffer.updateArray("revealers", revealerData.revealerPositions);
            uniformBuffer.updateArray("ranges", revealerData.revealerRanges);
            uniformBuffer.updateInt("revealersCount", FogOfWarPluginMaterial.revealerData.length);
        }
    }

    getCustomCode(shaderType:any) {
        if (shaderType === "vertex") {
            return {
                CUSTOM_VERTEX_DEFINITIONS: `
                    varying vec3 vWorldPos;
                `,
                CUSTOM_VERTEX_MAIN_END: `
                    vWorldPos = worldPos.xyz; 
                `
            }
        }

        if (shaderType === "fragment") {
            return {
                CUSTOM_FRAGMENT_DEFINITIONS: `
                    varying vec3 vWorldPos;
                `,
                CUSTOM_FRAGMENT_MAIN_END: `
                    float fogless = 0.4;
                    for(int i = 0; i < revealersCount; i++) {
                        int revealersId = i*2;

                        //Preprocess efficiency step
                        float xDist = vWorldPos.x - revealers[revealersId];

                        //Break if dist too great as positions ordered by X value
                        if(xDist < -maxRevealerRange) {
                            break;
                        }

                        //x dist too large?
                        float maxRange = ranges[i];
                        if(xDist > maxRange || xDist < -maxRange) {
                            continue;
                        }

                        float distance = length(vWorldPos.xz - vec2(revealers[revealersId],revealers[revealersId+1]));
                        float tmpFogless = (maxRange - distance) / ranges[i];
                        tmpFogless = clamp(tmpFogless, 0.0, 1.0);
                        if(tmpFogless > 0.1) {
                            tmpFogless = 1.0;
                        } else if(tmpFogless > 0.0) {
                            tmpFogless = tmpFogless/0.1;
                        }
                        if(tmpFogless > fogless) {
                            fogless = tmpFogless;
                        }
                    }
                    gl_FragColor.rgb *= vec3(fogless);
                `
            };
        }
        return null;
    }
}

```

[Previous page](https://forum.babylonjs.com/t/rts-fog-of-war/35479.md?page=1)
