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).