Subtle Development Space
Subtle Development Space

Reputation: 1254

GLSL array uniform with a dynamic length

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: enter image description here

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

Answers (1)

user128511
user128511

Reputation:

Make multiple ShaderMaterials, one for each number of VPOINTSMAX you want to support. Change the material on the Meshs 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

Related Questions