Search code examples
texturesfragment-shaderwgpu-rswgsl

How to conditionally sample a texture in WGSL?


I have a shader with N bind groups for N textures, and I want to sample only one of them at the time in my fragment shader based on a texture index.

Is it possible to do any of the following in WGSL?

  1. Bind each separate texture bind group to an element of an array, so I can access the array
  2. Conditionally sample only one texture. I tried this one as the example below, and I get a validation error. Also it doesn't seem feasible to maintain this approach if I have a lot of textures.
@group(0) @binding(0)
var texture0: texture_2d<f32>;
@group(0) @binding(1)
var sampler0: sampler;
@group(1) @binding(0)
var texture1: texture_2d<f32>;
@group(1) @binding(1)
var sampler1: sampler;

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    var texture_sample: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);
    if (in.texture_index == 1u) {
        texture_sample = textureSample(texture1, sampler1, in.coordinates);
    } else {
        texture_sample = textureSample(texture0, sampler0, in.coordinates);
    }
    return texture_sample;
}

With this I get:

Shader validation error: 
    ┌─ Shader:125:17
    │
125 │         texture_sample = textureSample(texture1, sampler1, in.coordinates);
    │                 ^^^^^^^^^^^^^ naga::Expression [108]

Solution

  • Assuming the naga validation error you're getting is about uniformity, you have three options for making it compile:

    1. Use textureSampleLevel instead of textureSample, which doesn't have the same uniformity requirements
    2. Store your texture index in a uniform variable instead of the vertex output which is a varying value
    3. Perform all the texture samples regardless of branch, and just use the branch to choose which sample you want:
    @fragment
    fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
        let texture_sample_1 = textureSample(texture1, sampler1, in.coordinates);
        let texture_sample_2 = textureSample(texture0, sampler0, in.coordinates);
    
        var texture_sample: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);
        if (in.texture_index == 1u) {
            texture_sample = texture_sample_1;
        } else {
            texture_sample = texture_sample_2;
        }
        return texture_sample;
    }