WebGL2 getUniformLocation 在 Android 上返回 null

WebGL2 getUniformLocation returns null on Android

提问人:user3722952 提问时间:9/28/2023 更新时间:11/16/2023 访问量:90

问:

这个赏金已经结束了。这个问题的答案有资格获得 +50 声望赏金。赏金宽限期在 14 小时后结束。User3722952 希望引起人们对此问题的更多关注

我对此感到非常困惑。我有一个带有少量制服的片段着色器。我正在获取制服的位置,如下所示:

    this.gl.useProgram(this.program)
    
    // Get uniform locations
    for (const uniform of [
      'u_resolution',
      'u_data_resolution',
      'u_pix_to_map',
      'u_gps_to_data',
      'u_data',
      'u_colormap',
      'u_product_min',
      'u_product_max',
      'u_product_cutoff',
      'u_alpha',
      'u_no_data',
    ]) {
      const loc = this.gl.getUniformLocation(this.program, uniform)

      if (loc === null) {
        throw Error(`could not get location for uniform ${uniform}`)
      }

      this.uniformLocations[uniform] = loc
    }

我的片段着色器如下:

#version 300 es

precision mediump float;

const float E = 2.71828182845904523536028747135266250;
const float PI = 3.14159265358979323846264338327950288;
const float RADIUS = 6378137.0;
const float HALF_SIZE = PI * RADIUS;

uniform vec2 u_resolution;
uniform vec2 u_data_resolution;
uniform mat3 u_pix_to_map;
uniform mat3 u_gps_to_data;
uniform sampler2D u_data;
uniform sampler2D u_colormap;
uniform float u_product_min;
uniform float u_product_max;
uniform float u_product_cutoff;
uniform float u_alpha;
uniform float u_no_data;

out vec4 fragColor;

vec2 apply(mat3 transform, vec2 coordinate) {
    float x = (transform[0][0] * coordinate.x) + (transform[0][1] * coordinate.y) + transform[0][2];
    float y = (transform[1][0] * coordinate.x) + (transform[1][1] * coordinate.y) + transform[1][2];

    return vec2(x, y);
}

vec2 mapToGPS(vec2 coordinate) {
    return vec2(
        (180.0 * coordinate.x) / HALF_SIZE,
        (360.0 * atan(exp(coordinate.y / RADIUS))) / PI - 90.0
    );
}

vec2 screenSpaceToCanvasSpace(vec2 coordinate, vec2 resolution) {
    return vec2(
        coordinate.x,
        -1.0 * (coordinate.y - resolution.y)
    );
}

float getDataValue(vec2 coords) {
    return texture(u_data, vec2(coords.x / u_data_resolution.x, coords.y / u_data_resolution.y)).x;
}

float bilerp(float x, float y, float x1, float x2, float y1, float y2, float z11, float z12, float z21, float z22) {
    // Sometimes the floor and ceiling values are too close to each other; if this is the case, just return the average.
    if (abs(x2 - x1) < 0.00001 || abs(y2 - y1) < 0.00001) { 
        return (z11 + z12 + z21 + z22) / 4.0;
    }

    float zxy1 = (( (x2 - x) / (x2 - x1) ) * z11) + (( (x - x1) / (x2 - x1) ) * z21);
    float zxy2 = (( (x2 - x) / (x2 - x1) ) * z12) + (( (x - x1) / (x2 - x1) ) * z22);

    return (( (y2 - y) / (y2 - y1) ) * zxy1) + (( (y - y1) / (y2 - y1) ) * zxy2);
}

float normalizeValue(float value) {
    return (value - u_product_min) / (u_product_max - u_product_min); 
}

vec3 getColor(float normalizedValue) {
    return texture(u_colormap, vec2(normalizedValue, 0.5)).rgb;
}

void main() {
    vec2 adjustedCoords = screenSpaceToCanvasSpace(gl_FragCoord.xy, u_resolution);

    vec2 mapCoords = apply(u_pix_to_map, adjustedCoords);

    vec2 gpsCoords = mapToGPS(mapCoords);

    vec2 dataCoords = apply(u_gps_to_data, gpsCoords);

    if (dataCoords.x < 0.0 || dataCoords.x > u_data_resolution.x || dataCoords.y < 0.0 || dataCoords.y > u_data_resolution.y) {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }

    float x1 = floor(dataCoords.x);
    float x2 = ceil(dataCoords.x);
    float y1 = floor(dataCoords.y);
    float y2 = ceil(dataCoords.y);    

    float z11 = getDataValue(vec2(x1, y1));
    float z12 = getDataValue(vec2(x1, y2));
    float z21 = getDataValue(vec2(x2, y1));
    float z22 = getDataValue(vec2(x2, y2));

    if (abs(z11 - u_no_data) < 0.01) {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }

    if (abs(z12 - u_no_data) < 0.01) {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }

    if (abs(z21 - u_no_data) < 0.01) {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }

    if (abs(z22 - u_no_data) < 0.01) {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }


    float value = bilerp(dataCoords.x, dataCoords.y, x1, x2, y1, y2, z11, z12, z21, z22);

    float normalizedValue = normalizeValue(value);

    vec3 color = getColor(normalizedValue);

    float normalizedCutoff = normalizeValue(u_product_cutoff);

    float alpha = u_alpha;

    if (normalizedValue < normalizedCutoff) {
        alpha -= (u_alpha * ((normalizedCutoff - normalizedValue) / normalizedCutoff));
    }

    fragColor = vec4(color, alpha);
}

在所有其他设备和浏览器(Windows、Linux、Mac、iOS、Firefox、Safari、Chrome)上,此代码可以正常工作,并且可以正确获取统一位置。

仅在 Android(Firefox 和 Chrome)上,并返回 null。另外值得注意的是,我无法在运行 API 34 的 Android 模拟器中重现这一点。我只在运行 Android 11 或 Android 12 的物理设备上看到过这种情况。u_resolutionu_pix_to_map

制服肯定是用到的,所以它们不应该在着色器之外进行优化。

如果在编译并运行着色器后设置断点:

this.gl.getActiveUniform(this.program, 0)

结果是:

WebGLActiveInfo {size: 1, type: 35664, name: 'u_resolution'}

这告诉我,这种统一存在于编译的着色器中。但是,如果我试图在之后立即获得那套制服:

this.gl.getUniformLocation(program, 'u_resolution')

结果是 。null

另外值得注意的是,似乎没有错误:

this.gl.getError()
0

我不确定如何进一步调试,但我能想到的唯一可能性是:

  • Android 上的 GLSL 编译器错误地从编译的着色器中删除了均匀
  • 我做了一些事情来打破 Android 上的制服“包装”
  • Android 上的 Chrome 和/或 Firefox 中还有其他一些错误

知道问题是什么,或者甚至只是我可以采取一些进一步的步骤来调试这个问题吗?

Android TypeScript GLSL WebGL

评论

0赞 Rabbid76 11/18/2023
您确定您的 Android 设备上有“webgl2”上下文吗?
0赞 user3722952 11/19/2023
@Rabbid76是的,我已经证实了这一点。我能够获取上下文并编译着色器。这似乎基本上影响了我尝试过的所有 Android 设备,除了运行 API 34 的模拟器。多个好友设备、其他模拟器和我的测试设备都出现此问题。我怀疑这可能是 Android 上 WebGL 编译器/链接器中的一个错误。
0赞 Rabbid76 11/19/2023
制服的大小受每个着色器的限制,并取决于硬件。您可以在 Android 设备上超过此限制。
0赞 user3722952 11/30/2023
@Rabbid76根据 webglreport.com/?v=2,在测试的 Android 设备上,我拥有片段着色器的所有最大值都大于或等于工作设备上报告的最大值。

答: 暂无答案