Униформа массива GLSL с динамической длиной - PullRequest
0 голосов
/ 04 апреля 2019

Я пытаюсь внедрить функцию «туман войны» в мою стратегическую игру. Я читал некоторые другие вопросы и ответы по 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 Ответ

2 голосов
/ 04 апреля 2019

Создайте несколько ShaderMaterial с, по одному на каждое число VPOINTSMAX, которое вы хотите поддерживать.Измените материал на Mesh s, если вы хотите использовать другой VPOINTSMAX

GLSL не поддерживает массивы переменного размера.

Единственное другое решение - установить VPOINTSMAX на самый большой размер, который вы когда-либо будете использовать, а затем прервать цикл for, как на

uniform int maxPoints;

#define VPOINTSMAX 20

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

Но похоже, что так и будетбыть медленнее, поскольку циклы часто развертываются в графических процессорах

...