未使用的阴影采样器的未定义行为

Undefined Behaviour with unused Shadow Samplers

我有一个在延迟渲染器中执行光照通道的着色器。它需要统一的各种可选功能,其中之一是阴影。

我想使用相同的着色器来执行带阴影和不带阴影的照明(参见我的其他问题 )。这很好用。

但是,当我渲染没有阴影的光时出现问题,因此不将任何纹理绑定到阴影采样器,而留下一些其他纹理绑定。在我的系统上,运行 NVIDIA 346.59 drivers,这会产生以下 GL error/warning:

Program undefined behavior warning: Sampler object 0 is bound to non-depth texture 0, yet it is used with a program that uses a shadow sampler . This is undefined behavior.

尽管我知道如果是这种情况就不会使用采样器

有什么办法可以解决这个问题吗?似乎不必绑定一些未使用的占位符纹理来抑制此警告。

编辑:请注意,这确实有效。唯一的问题是 driver.

发出的警告

编辑:好的,这是我的着色器。与这个问题相关的部分是 main() 函数。

#version 330
#extension GL_ARB_shading_language_420pack : enable

#include "headers/blocks/camera"
#include "headers/blocks/spotlight"
#include "headers/uniform_disks"
#include "headers/shadow/sample_spot"

in vec2 texcrd;

layout(std140, binding=0) uniform CAMERABLOCK { CameraBlock CB; };
layout(std140, binding=1) uniform SPOTLIGHTBLOCK { SpotLightBlock LB; };

uniform int mode;
uniform int shadQuality;
uniform int shadFilter;
layout(binding=0) uniform sampler2D texDiff;
layout(binding=1) uniform sampler2D texNorm;
layout(binding=2) uniform sampler2D texSurf;
layout(binding=3) uniform sampler2D texSpec;
layout(binding=4) uniform sampler2D texAmbi;
layout(binding=5) uniform sampler2D texDepth;
layout(binding=6) uniform sampler2DShadow texShad;

out vec3 fragColour;


vec3 get_view_pos(in vec2 _tc) {
    float dep = texture(texDepth, _tc).r * 2.f - 1.f;
    vec4 p_pos = vec4(_tc * 2.f - 1.f, dep, 1.f);
    vec4 v_pos = CB.invProj * p_pos;
    return v_pos.xyz / v_pos.w;
}

float get_shadow_value(vec3 _wpos, vec3 _wsurf) {
    vec3 normPos = _wpos + _wsurf*0.04f;
    vec4 sc = LB.matrix * vec4(normPos, 1.f);
    vec3 shadcrd = sc.xyz / sc.w * 0.5f + 0.5f;

    float bias = get_bias(_wsurf, normalize(normPos - LB.position));
    if (shadFilter < 2) return sample_shadow(shadcrd, bias, texShad);
    else {
        if (shadQuality == 0) return sample_shadow_x4(shadcrd, bias, texShad);
        if (shadQuality == 1) return sample_shadow_x8(shadcrd, bias, texShad);
        if (shadQuality == 2) return sample_shadow_x16(shadcrd, bias, texShad);
    }
}

vec3 get_diffuse_value(vec3 _lightDir, vec3 _normal) {
    vec3 txDiff = texture(texDiff, texcrd).rgb;
    float dotProd = max(dot(-_lightDir, _normal), 0.f);
    return LB.colour * txDiff * dotProd;
}

vec3 get_specular_value(vec3 _lightDir, vec3 _normal, vec3 _position) {
    vec3 txSpec = texture(texSpec, texcrd).rgb;
    vec3 reflection = reflect(_lightDir, _normal);
    vec3 dirFromCam = normalize(-_position);
    float factor = pow(max(dot(dirFromCam, reflection), 0.f), 50.f);
    return LB.colour * txSpec * factor;
}

void main() {
    vec3 v_pos = get_view_pos(texcrd);
    vec3 v_norm = normalize(texture(texNorm, texcrd).rgb * 2.f - 1.f);
    vec3 lightDir = normalize(v_pos - vec3(CB.view * vec4(LB.position, 1.f)));
    if (dot(-lightDir, v_norm) < -0.25f) discard;

    vec4 wp = CB.invView * vec4(v_pos, 1.f);
    vec3 w_pos = wp.xyz / wp.w;
    vec3 v_surf = normalize(texture(texSurf, texcrd).rgb * 2.f - 1.f);
    vec3 w_surf = normalize(vec3(CB.trnView * vec4(v_surf, 0.f)));

    vec3 spotDir = normalize(vec3(CB.view * vec4(LB.direction, 0.f)));
    float spotDist = distance(LB.position, w_pos);
    float angle = acos(dot(lightDir, spotDir));
    if (angle > LB.angle || spotDist > LB.intensity) discard;

    bool doShad = bool(mode & 1);
    bool doDiff = bool(mode & 2);
    bool doSpec = bool(mode & 4);

    float shad = 1.f;
    if (doShad) shad = get_shadow_value(w_pos, w_surf);
    if (shad == 0.f) discard;

    vec3 diff = vec3(0.f, 0.f, 0.f);
    if (doDiff) diff = get_diffuse_value(lightDir, v_norm);

    vec3 spec = vec3(0.f, 0.f, 0.f);
    if (doSpec) spec = get_specular_value(lightDir, v_norm, v_pos);

    float fovRolf = pow((LB.angle - angle) / (1.f - LB.angle), sqrt(LB.softness));
    float dstRolf = 1.f - spotDist / LB.intensity;
    float rolloff = fovRolf * dstRolf;

    fragColour = (diff + spec) * shad * rolloff;
}

你的另一个问题我也看了

您通常会做的是使用所有 required/used 采样器编译着色器,并且不公开任何未使用的采样器。这样做很可能会给您带来错误。

我知道您不喜欢这种方法,但可以使用 ifdefs 执行此操作并创建不同版本的着色器以考虑 shadow/noshadow 灯光。您可以自己缓存它们并在运行时在 CPU 中切换它们。在 CPU 中进行分支比在 GPU 中进行更好;请记住,您只会为对象执行一次此操作,而在 GPU 中,您将为每个像素执行一次,这是非常浪费的。

类似于

#if defined(USE_SHADOWS)
sampler2D [...]
#endif

在 运行 研究这个问题并进行了一些挖掘之后,我发现了另一种不需要您使用宏的方法。所以我们的想法是简单地解决警告。我创建了一个 1x1 和 GL_DEPTHCOMPONENT16 类型的假纹理,并将过滤模式设置为:

glTexParameteri(mTextureTarget, GL_TEXTURE_MIN_FILTER, supportsMipMaps && getTextureInfo().mMipCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(mTextureTarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(mTextureTarget, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

如果你想优化它,你可以在你的 ShaderObject class(或代表 GLShader 的 w/e 对象)中做,在你的着色器中反射制服时,见如果你有一个 shadowSampler2D 类型(我总是将我的硬编码到第 15 单元并且我正在寻找它)。如果您确实拥有该类型并且在渲染调用期间(就在您绑定着色器之前),您仍然没有绑定单元 15,请将您的假纹理与过滤方法一起绑定。

此外,您还需要在清除其他内容之前调用 glUseProgram(0),任何具有该着色器的程序仍会发出警告。