本文介绍了具有动态长度的GLSL阵列均匀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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)
]

并使用ShaderMaterial均匀地通过视点.由于数组的长度可以是任何值,因此我在顶点着色器代码的开头注入了VPOINTSMAX常量:

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

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

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

如果in_vision值不大于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:

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

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.

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

推荐答案

制作多个ShaderMaterial,每个要支持的VPOINTSMAX数均一个.当您要使用其他VPOINTSMAX

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不支持可变大小的数组.

GLSL does not support variable sized arrays.

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

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

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

But that seems like it would be slower since loops are often unrolled in GPUs

这篇关于具有动态长度的GLSL阵列均匀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 06:45