1
$\begingroup$

How can I avoid having to declare the sampler outside of the if statement in this shader code sample?

let sample = textureSample(r_texture, r_sampler, in.tex_coords);
if in.use_texture {
    return sample * in.color;
} else {
    return in.color;
}

This came up as an answer to another of my questions, and it was suggested that the sampler needed to be declared here due to uniform control flow, but that it could be avoided.

How to avoid it?

$\endgroup$
1
  • $\begingroup$ This more of avoiding calling the textureSample function outside an if statement then declaring it. $\endgroup$
    – pmw1234
    Sep 25, 2023 at 20:10

1 Answer 1

3
$\begingroup$

Background

In order to support mipmapping, the texture sampling system needs to know what scale the texture is being displayed at. In order to determine that scale automatically, the derivatives (or “gradient”) of the texture coordinates are used. The way GPUs calculate derivatives is by comparing the results of the calculations performed in one fragment to adjacent fragments.

The reason you can't put textureSample inside of if is because it is possible that the if takes different branches on different pixels, and so the derivative won't exist. “Uniform control flow” means “all fragments are guaranteed to execute this code”.

Possible solutions

  • If r_texture has no mipmaps, or if r_sampler is configured to prevent using them, then you don't need to select a mipmap level. Use textureSampleLevel(r_texture, r_sampler, in.tex_coords, 0.0) — the additional argument is the mipmap level, and 0 means the full-resolution texture, which is all you want in this case.

  • If you do wish to use mipmaps, then you can compute the relevant derivatives outside of the if, and use them inside:

    let ddx = dpdx(in.tex_coords);
    let ddy = dpdy(in.tex_coords);
    if in.use_texture {
        let sample = textureSampleGrad(r_texture, r_sampler, in.tex_coords, ddx, ddy);
        return sample * in.color;
    } else {
        return in.color;
    }
    

    This is basically doing what textureSample would do implicitly, but moving just the parts that can't be in the if outside.

Language notes

The code in this answer is WGSL. Equivalent functions in GLSL:

WGSL GLSL
textureSample() texture()
textureSampleGrad() textureGrad()
dpdx() dFdx()
dpdy() dFdy()

Also, here's another introduction to the same concepts from the Khronos OpenGL wiki (which uses GLSL).

$\endgroup$
1
  • $\begingroup$ Thanks, that was very insightful. FWIW, I found that the mipmap level in textureSampleLevel() needs to be specified as a float. $\endgroup$
    – junglie85
    Sep 26, 2023 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.