Hello everyone,
Introduction
I wan’t to be able to create a sort of fog of war for my app using shaders. I feel that I might be close, but I cant figure out how to make it work… (maybe I don’t have the right approach all together lol, I hope not)
Here is the context:
-
I have a plane that has a canvas texture. The user will be able to draw in black on this plane.
We call it the “board”. -
I have objects that slide on this plane.
We call them “tokens”.
What I want:
-
I want to set invisibe the tokens once they are over a part of the board that is black.
-
If the token is half in the black part, half in the non-black part, only the part of the token over the black part will be invisible.
What I implemented so far
Typescript part:
// Create canvas element and color a half of it black, the other half transparent
this.FowCanvas = document.createElement('canvas');
this.fowContext = this.FowCanvas.getContext('2d');
this.fowContext.fillStyle = 'rgb(0, 0, 0)';
this.fowContext.fillRect(0, 0, this.FOWCanvas.width/2, this.FOWCanvas.height);
// Create board
const texture = new THREE.CanvasTexture(this.FowCanvas);
const material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true
});
const board= new THREE.Mesh(
new THREE.PlaneBufferGeometry(canvas.width / WORLD_UNIT, canvas.height / WORLD_UNIT),
material
);
board.receiveShadow = false;
board.rotation.x = - Math.PI * 0.5;
board.position.y = BOARD_POSITION_Y; // this is like 0.05 for pixel fighting purposes
// Applying shader to my object's materials
token.traverse((object) => {
const material = this.board.material as THREE.MeshBasicMaterial;
if (object instanceof THREE.Mesh) {
if (object.material instanceof Array) {
for (let i = 0; i < object.material.length; i ++) {
this.applyFOWShader(object.material[i], material.map);
}
} else {
this.applyFOWShader(object.material, material.map);
}
});
applyFOWShader(material, texture) {
material.transparent = true;
material.onBeforeCompile = (shader) => {
fowShader(texture, shader);
};
}
Now, here is the actual shader
const fowShader = (texture, shader) => {
shader.uniforms.uTexture = texture;
// Vertex Shader overwrite before Main() for uniforms and varyings declaration
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
varying vec4 vFragPos;
`
);
// Vertex Shader overwrite inside Main() for calculations
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
vec4 vFragPos = modelMatrix * vec4(position, 1.0);
`
);
// Fragment Shader before Main() for uniforms and varyings declaration
shader.fragmentShader = shader.fragmentShader.replace(
'#include <common>',
`
#include <common>
uniform sampler2D uTexture;
varying vec4 vFragPos;
`
);
// Fragment Shader inside Main() for calculations
shader.fragmentShader = shader.fragmentShader.replace(
'#include <output_fragment>',
`
#ifdef OPAQUE
diffuseColor.a = 1.0;
#endif
// https://github.com/mrdoob/three.js/pull/22425
#ifdef USE_TRANSMISSION
diffuseColor.a *= transmissionAlpha + 0.1;
#endif
vec4 texColor = texture2D(uTexture, vFragPos.xy);
if (texColor.a != 0.0) {
diffuseColor.a = 0.0;
}
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
`
);
};
export default fowShader;
So there you go.
TLDR would be this
vec4 texColor = texture2D(uTexture, vFragPos.xy);
if (texColor.r == 0.0 && texColor.g == 0.0 && texColor.b == 0.0) {
diffuseColor.a = 0.0;
}
gl_FragColor = vec4( outgoingLight, diffuseColor.a );
I try to get the texColor of my boardTexture at the position of my token.
If this color is not transparent, then I want to have the color of my token transparent 100%.
Some images to show the status
Note: the battlemap that you see next to the black can be ignored, it is the actual board with a battlemap and is not used in this context.
Note2: You can see that the canvas texture is well updated and visible on the scene. I think it is being sent to the shader without issue. So maybe the problem is in the shader itself? idk…
So there it is. I am quite new to threejs and shaders, so maybe all this does not make sense at all and maybe a better approach could be used…
Right now I am hopeless!
Thank you for reading this! 
