首页 > 解决方案 > 在 Three.js 中指定 WebGL2 上下文后,无法遍历 sampler2D 数组

问题描述

我一直sampler2D在我的片段着色器中使用一个数组(这些是阴影贴图,最多可以有 16 个,所以当然,一个数组比使用 16 个单独的变量更可取)。然后我const context = canvas.getContext('webgl2');将WebGL2 上下文(THREE.WebGLRendererarray index for samplers must be constant integral expressions

uniform sampler2D samplers[MAX_SPLITS];

for (int i = 0; i < MAX_SPLITS; ++i) {
    if (i >= splitCount) {
        break;
    }

    if (func(samplers[i])) { // that's where the error happens
      ...
    }
}

真的没有办法解决这个问题吗?我必须使用十六个单独的变量吗?

(着色器中没有直接#version指令,但 THREE.js 似乎#version 300 es默认添加了一个)

标签: glslwebgl2

解决方案


您不能在 GLSL ES 3.0 中对采样器使用动态索引。从规格

12.29 采样器

应该允许采样器作为 l 值吗?该规范已经允许等效行为:

当前规格:

uniform sampler2D sampler[8];
int index = f(...);
vec4 tex = texture(sampler[index], xy); // allowed

使用采样器类型的分配:

uniform sampler2D s;
s = g(...);
vec4 tex = texture(s, xy); // not allowed

解决方案:规范现在禁止对采样器数组进行动态索引。将采样器数组的索引限制为常量整数表达式。

12.30 动态索引

对于 GLSL ES 1.00,不强制支持数组、向量和矩阵的动态索引,因为某些实现不直接支持它。软件解决方案(通过程序转换)适用于部分案例,但会导致性能不佳。GLSL ES 3.00 是否应该强制支持动态索引?

解决方案:强制支持数组的动态索引,但采样器数组、片段输出数组和统一块数组除外。

GLSL ES 3.00 是否应该强制支持向量和矩阵的动态索引?

解决方案:是的。

GLSL ES 1.00 支持通过常量索引表达式对采样器数组进行索引。常量索引表达式是由常量表达式和某些循环索引形成的表达式,为循环构造的子集定义。这个功能应该包含在 GLSL ES 3.00 中吗?

解决方案:不可以。采样器数组只能由常量积分表达式索引。

您可以使用 2D_ARRAY 纹理来解决您的问题吗?将您当前的每个 2D 纹理放入 2D_ARRAY 纹理的层中,然后 z 坐标只是一个整数层索引。优点是,您可以使用更多带有 2D_ARRAY 的图层,然后获得采样器。WebGL2 实现通常只有 32 个采样器,但在 2D_ARRAY 纹理中允许数百或数千层。

或使用 GLSL 1.0

const vs1 = `
void main() { gl_Position = vec4(0); }
`;
const vs3 = `#version 300 es
void main() { gl_Position = vec4(0); }
`;
const fs1 = `
precision highp float;

#define MAX_SPLITS 4

uniform sampler2D samplers[MAX_SPLITS];
uniform int splitCount;

bool func(sampler2D s) {
  return texture2D(s, vec2(0)).r > 0.5;
}

void main() {
  float v = 0.0;
  for (int i = 0; i < MAX_SPLITS; ++i) {
      if (i >= splitCount) {
          break;
      }

      if (func(samplers[i])) { // that's where the error happens
         v += 1.0;
      }
  }
  gl_FragColor = vec4(v);
}
`;

const fs3 = `#version 300 es
precision highp float;

#define MAX_SPLITS 4

uniform sampler2D samplers[MAX_SPLITS];
uniform int splitCount;

bool func(sampler2D s) {
  return texture(s, vec2(0)).r > 0.5;
}

out vec4 color;

void main() {
  float v = 0.0;
  for (int i = 0; i < MAX_SPLITS; ++i) {
      if (i >= splitCount) {
          break;
      }

      if (func(samplers[i])) { // that's where the error happens
         v += 1.0;
      }
  }
  color = vec4(v);
}
`;

function main() {
  const gl = document.createElement('canvas').getContext('webgl2');
  if (!gl) {
    return alert('need WebGL2');
  }
  
  test('glsl 1.0', vs1, fs1);
  test('glsl 3.0', vs3, fs3);
  
  function test(msg, vs, fs) {
    const p = twgl.createProgram(gl, [vs, fs]);
    log(msg, ':', p ? 'success' : 'fail');
  }
}
main();


function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>


推荐阅读