GLSL数组具有动态长度

时间:2019-04-04 15:39:34

标签: arrays three.js glsl

我正在尝试在我的战略游戏中实现“战争迷雾”功能。我一直在阅读关于SO的其他问答,我认为我需要使用自定义GLSL着色器。所以我定义了一个包含“视觉点”的数组:

let vision_points = [
  new THREE.Vector2(1500, 1500),
  new THREE.Vector2(1500, -1500),
  new THREE.Vector2(-1500, 1500),
  new THREE.Vector2(-1500, -1500)
]

并使用ShaderMaterial作为制服传递视点。由于数组长度可以是任何值,因此我在顶点着色器代码的开头注入了VPOINTSMAX常量:

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
});

然后,顶点着色器计算顶点本身与每个vision_point之间的距离。然后根据顶点是否在视觉范围内,将varying float in_vision设置为0.0或1.0:

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);
}

最后,如果in_vision值不大于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;
  }
}

所以我得到了想要的结果: enter image description here

现在的问题是,在游戏运行时,vision_points数组的内容及其长度都会发生变化。更改统一值应该没有任何麻烦,但是随着数组长度的变化,我需要找到一种重新定义VPOINTSMAX常量的方法。到目前为止,我唯一想到的解决方案是重建ShaderMaterial并使用注入的新VPOINTSMAX值重新编译着色器代码。然后只需将新材料应用于网格即可。但是我担心这可能会导致性能问题。

您还建议其他哪些方法来将长度动态变化的数组统一更新/传递到GLSL着色器中?

1 个答案:

答案 0 :(得分:2)

制作多个ShaderMaterial,每个要支持的VPOINTSMAX个。当您想使用其他Mesh

时,请更改VPOINTSMAX上的材质

GLSL不支持可变大小的数组。

唯一的其他解决方案是将VPOINTSMAX设置为您将使用的最大大小,然后像on一样退出for循环

uniform int maxPoints;

#define VPOINTSMAX 20

...
  for (int v = 0; v < VPOINTSMAX; v++) {
    if (v >= maxPoints) {
     break;
    }
    ...
  }

但是这似乎会比较慢,因为循环通常会在GPU中展开