Reputation: 1254
I'm trying to implement a "fog of war" feature into my strategy game. I've been reading some other Q&A on SO and I figured that I need to use custom GLSL shaders. So I defined and array containing "vision points":
let vision_points = [
new THREE.Vector2(1500, 1500),
new THREE.Vector2(1500, -1500),
new THREE.Vector2(-1500, 1500),
new THREE.Vector2(-1500, -1500)
]
And used ShaderMaterial
passing in the vision points as a uniform. Since the arrays length might be any value, I inject VPOINTSMAX
constant at the beginning of the vertex shader code:
let material = new THREE.ShaderMaterial({
uniforms: {
u_texture: {type: 't', value: this.texture},
vision_points: {
type: 'v2v',
value: vision_points
}
},
vertexShader: "#define VPOINTSMAX "+vision_points.length+"\n"+fow_vs,
fragmentShader: fow_fs
});
Then the vertex shader calculates the distance between the vertex itself and each vision_point. Then it sets a varying float in_vision
to 0.0 or 1.0, depending if the vertex is in vision range:
uniform vec2 vision_points[VPOINTSMAX];
varying vec2 v_texCoord;
varying float in_vision;
void main() {
in_vision = 0.0;
for (int v = 0; v < VPOINTSMAX; v++) {
vec2 pos2 = vec2(position.x, position.z);
float distance = length(pos2-vision_points[v]);
if (distance < 1000.0) {
in_vision = 1.0;
}
}
// Pass the texcoord to the fragment shader.
v_texCoord = uv;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
Lastly the fragment shader darkens the color if in_vision value is not more than 0:
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying float in_vision;
void main() {
vec4 color = texture2D(u_texture, v_texCoord);
if (in_vision > 0.0) {
gl_FragColor = color;
} else {
gl_FragColor = color/2.0;
}
}
And so I got the result I wanted:
Now the problem is that, while the game will be running, the contents of vision_points
array will be changing as well as its length. There shouldn't be any trouble changing the uniform value, but as the length of array will be changing, I need to find a way to redefine the VPOINTSMAX
constant. The only solution that I could think of so far is reconstructing the ShaderMaterial
and recompiling the shader code with a new VPOINTSMAX
value injected. Then simply applying a new material to the mesh. But I fear that this might cause performance issues.
What other approaches would you suggest for updating/passing an array with a dynamically changing length into the GLSL shader as a uniform?
Upvotes: 1
Views: 3315
Reputation:
Make multiple ShaderMaterial
s, one for each number of VPOINTSMAX
you want to support. Change the material on the Mesh
s when you want to use a different VPOINTSMAX
GLSL does not support variable sized arrays.
The only other solution is to set VPOINTSMAX
to the largest size you'll ever use and then break out of your for loop as on
uniform int maxPoints;
#define VPOINTSMAX 20
...
for (int v = 0; v < VPOINTSMAX; v++) {
if (v >= maxPoints) {
break;
}
...
}
But that seems like it would be slower since loops are often unrolled in GPUs
Upvotes: 4