使用 i 访问纹理时 OpenGL ES 2 着色器链接失败

OpenGL ES 2 Shader linking fails when accessing texture using i

我正在尝试在我的着色器中添加对多个阴影贴图的支持,但当我尝试访问统一结构内的 sampler2D 时,它没有 linking。我的片段着色器:

#version 300 es

precision highp float;
precision highp int;

const int MAX_LIGHTS = 3;

#ifndef LIGHTINFO_STRUCT_H
#define LIGHTINFO_STRUCT_H
struct LightInfo {
    sampler2D shadow_map;
    vec3 reverse_light_direction;
    vec4 color;
    vec3 position;
    float intensity;
    mat4 texture_matrix;
};
#endif

in vec2 vtf_tex_coord;
in vec4 vtf_projected_tex_coords[MAX_LIGHTS];
in vec3 vtf_normal;
in vec3 vtf_frag_pos;

uniform sampler2D u_map_albedo;
uniform sampler2D u_map_normal;

uniform LightInfo u_lights[MAX_LIGHTS];
uniform int u_present_lights;

out vec4 out_color;

const vec4 AMBIENT_LIGHT = 0.1 * vec4(0.8, 0.8, 1, 1);
const float BIAS = -0.006;

float calculateLightIncidence(vec3 position, vec3 lightPosition, float intensity) {
    float d = distance(position, lightPosition);
    return intensity / (1.0 + d * d);
}

bool isInRange(vec2 tex_coord) {
    return tex_coord.x >= 0.0 && tex_coord.x <= 1.0 && tex_coord.y >= 0.0 && tex_coord.y <= 1.0;
}

void main() {
    vec3 testNormal = texture(u_map_normal, vtf_tex_coord).rgb;
    vec3 normal = normalize(vtf_normal);

    float fragmentDirectLight = 0.0;
    float fragmentLightMultiplier = 1.0;

    // calculate for all light sources
    for (int i = 0; i < 3; i++) {
        // if (i >= u_present_lights) break;
        fragmentDirectLight += dot(normal, u_lights[i].reverse_light_direction);

        // shadow map coords
        vec3 projectedShadowMapCoords = vtf_projected_tex_coords[i].xyz / vtf_projected_tex_coords[i].w;
        float projectedCurrentDepth = projectedShadowMapCoords.z + BIAS;
        float projectedDepth = texture(u_lights[i].shadow_map, projectedShadowMapCoords.xy).r;
        float projectedShadowLight = projectedCurrentDepth < projectedDepth ? 1.0 : 0.0;

        float shadowMultiplier = isInRange(projectedShadowMapCoords.xy) ? clamp(projectedShadowLight, 0.3, 1.0) : 1.0;
        fragmentLightMultiplier *= shadowMultiplier;
    }

    fragmentDirectLight = clamp(fragmentDirectLight, 0.1, 1.0);

    float at = calculateLightIncidence(vtf_frag_pos, u_lights[0].position, u_lights[0].intensity);

    vec4 tex_color = texture(u_map_albedo, vtf_tex_coord);
    vec3 col = mix(tex_color.rgb, u_lights[0].color.rgb, at);

    out_color = vec4(col * fragmentLightMultiplier * clamp(fragmentDirectLight, 0.3, 1.0), 1.0);
}

不要介意颜色的混乱,我正在努力让阴影贴图先起作用。

问题是:如果我换行

float projectedDepth = texture(u_lights[i].shadow_map, projectedShadowMapCoords.xy).r;

float projectedDepth = texture(u_lights[0].shadow_map, projectedShadowMapCoords.xy).r;

一切正常,link一切正常,显示正确。但是,如果我将其改回索引访问,它就不再 link。

需要说明的是,我的这部分代码报告了 link 错误:

static createProgram(gl: WebGL2RenderingContext, vertexShader: string, fragmentShader: string): WebGLProgram {
        const program = gl.createProgram();
        if (!program) throw `Failed to create shader program with shaders: ${vertexShader} and ${fragmentShader}`;

        gl.attachShader(program, this.createShader(gl, gl.VERTEX_SHADER, vertexShader));
        gl.attachShader(program, this.createShader(gl, gl.FRAGMENT_SHADER, fragmentShader));
        gl.linkProgram(program);

        const success = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (success)
            return program;

        gl.deleteProgram(program);
        throw `Failed to link shader program with shaders: \n\n${vertexShader}\n\n and \n\n${fragmentShader}\n\nError: ${gl.getError()}`
}

并打印错误代码 0。

所有制服都设置正确,为什么我将其更改为访问 u_lights[i].shadow_map 却没有 link?

还有那个评论的 break 是因为我试图将 for 循环设置为执行固定次数以查看是否是问题所在,然后才 for (int i = 0; i < u_present_lights; i++)

我没有遇到任何编译错误,只是 link 着色器失败了。

这是我的顶点着色器以防万一:

#version 300 es

precision highp float;
precision highp int;

const int MAX_LIGHTS = 3;

#ifndef LIGHTINFO_STRUCT_H
#define LIGHTINFO_STRUCT_H
struct LightInfo {
    sampler2D shadow_map;
    vec3 reverse_light_direction;
    vec4 color;
    vec3 position;
    int casts_shadows;
    float intensity;
    mat4 texture_matrix;
};
#endif

layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec2 a_uv;
layout (location = 2) in vec3 a_normal;

out vec2 vtf_tex_coord;
out vec4 vtf_projected_tex_coords[MAX_LIGHTS];
out vec3 vtf_normal;
out vec3 vtf_frag_pos;

uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;

uniform LightInfo u_lights[MAX_LIGHTS];
uniform int u_present_lights;

void main() {
    vec4 world_pos = u_model * vec4(a_pos, 1.0);
    gl_Position = u_projection * u_view * world_pos;
    vtf_tex_coord = a_uv;

    for (int i = 0; i < u_present_lights; i++) {
        vtf_projected_tex_coords[i] = u_lights[i].texture_matrix * world_pos;
    }
    
    vtf_normal = mat3(u_model) * a_normal;
    vtf_frag_pos = world_pos.xyz / world_pos.w;
}

OpenGLES 2 使用 GLSL ES 100。附录 A 第 4 节和第 5 节 here (p108) 涵盖了有关索引采样器的规则。规则强制支持 constant index expression,这基本上意味着您可以使用 hardcoded/constant 索引、统一或简单循环。您正在使用一个简单的循环,因此您满足了该条件。

但有些事情有点奇怪。你的标题提到了 OpenGLES 2,但是你的着色器指定了 #version 300 es,它只能在 OpenGLES 3 或更高版本上使用,所以我不太确定那里的情况。

GLSL ES 300 的规范是 here,它对索引采样器有更严格的规则。在 ES 300 中,规则仅为 constant integral expressions。这排除了在您执行操作时使用来自简单循环的索引。引用自规范:

Indexing of arrays of samplers by constant-index-expressions is supported in GLSL ES 1.00. A constant- index-expression is an expression formed from constant-expressions and certain loop indices, defined for a subset of loop constructs. Should this functionality be included in GLSL ES 3.00?

RESOLUTION: No. Arrays of samplers may only be indexed by constant-integral-expressions.

您可能无法手动展开循环。