<blockquote>
<p>How can I conditionally decide in the shader whether to sample from the texture or not?</p>
</blockquote>
<p>Abstractly, this is simple:</p>
<ul>
<li><p>Add a vertex attribute that specifies whether to use the texture or not. Set that attribute appropriately for your outline and your fill (which I understand to be made of separate triangles).</p>
</li>
<li><p>Pass the attribute on to the fragment shader.</p>
</li>
<li><p>In the fragment shader,</p>
<pre><code>let sample = textureSample(r_texture, r_sampler, in.tex_coords);
if in.use_texture {
    return sample * in.color;
} else {
    return in.color;
}
</code></pre>
</li>
</ul>
<p>(The <code>textureSample()</code> call is outside of the <code>if</code> because it needs to be in “uniform control flow”; this can be avoided but that's another entire topic.)</p>
<p>However, you don't necessarily need to introduce a separate attribute; you can use one that already exists. For example, you could give all the border triangles texture coordinates of <code>[-1, -1]</code>, which are clearly not meaningful to the texture (…unless you're using repeating textures). Or, if you never actually want to color-tint a texture, you could designate a particular <em>color</em> value to mean “use texture”.</p>
<hr />
<p>Or, instead, you can make sure your textures always have at least one white texel. This is a more appropriate solution if your application uses a texture atlas (unrelated textures arranged inside only one actual texture object) already.</p>
<hr />
<p>Or if you only ever want to draw rectangles, then you can stretch the texture coordinates a bit in your mesh construction and draw a border that way, instead of using extra triangles:</p>
<pre><code>if any(in.tex_coords &lt; 0.0) || any(in.tex_coords &gt; 0.0) {
    return sample * in.color;
} else {
    return in.color;
}
</code></pre>
<p>(Since you're using <code>wgpu</code> specifically, I'll mention that you can also use <a href="https://docs.rs/wgpu/0.17.0/wgpu/enum.AddressMode.html#variant.ClampToBorder" rel="nofollow noreferrer">clamp to border</a> to implement this directly in the sampler rather than in shader code. However, that's an extension and is not supported in WebGPU proper.)</p>

CC BY-SA 4.0
2023-09-03T16:33:50.217Z
I thought about both the flag field in the vertex and the tex coords options. I hadn’t considered the latter would have implications with repeating. I like the texture atlas option but think I’ll go with the vertex attribute for simplicity.
junglie85
CC BY-SA 4.0
2023-09-23T19:27:58.743Z
I’d be interested to know how to avoid declaring the sampler outside of the if statement - https://computergraphics.stackexchange.com/questions/13729/how-to-avoid-declaring-sampler-outside-of-statement
junglie85
CC BY-SA 4.0
2023-09-02T13:43:47.833Z
2023-09-02T13:43:47.833Z
Kevin Reid
<p>I'm building a little 2D graphics library in Rust/WGPU as a means to learn graphics programming. I want to draw an outline around a shape and specify the outline's color. All of my shapes have a texture, either explicitly set or a 1x1 white pixel otherwise. When I draw a shape with the default white texture, the outline is as expected but when I do the same with another texture set, it samples the color and sets the outline to it.</p>
<p>Here's  an example of the problem: the central rectangle has a white outline as desired, but the top left square has a red outline but it should be white.</p>
<p><a href="https://i.sstatic.net/EWhOT.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/EWhOT.png" alt="Example showing correct and incorrect behaviour" /></a></p>
<p>The outline calculation is based on <a href="https://stackoverflow.com/questions/68973103/how-to-create-outline?noredirect=1&amp;lq=1">this answer</a> and I'm calculating a triangle list so that I can use the same pipeline as the main shape which is also using a triangle list. Relevant aspects of my shaders and data structures are as follows:</p>
<pre><code>struct Vertex {
    @location(0) coords: vec2&lt;f32&gt;,
    @location(1) color: vec4&lt;f32&gt;,
    @location(2) tex_coords: vec2&lt;f32&gt;,
}

struct VsOut {
    @builtin(position) position: vec4&lt;f32&gt;,
    @location(0) color: vec4&lt;f32&gt;,
    @location(1) tex_coords: vec2&lt;f32&gt;,
}

@vertex
fn vs_main(vertex: Vertex) -&gt; VsOut {
    ...

    return VsOut(
        vertex.coords,
        vertex.color,
        vertex.tex_coords,
    );
}

@group(1)
@binding(0)
var r_sampler: sampler;
@group(1)
@binding(1)
var r_texture: texture_2d&lt;f32&gt;;

@fragment
fn fs_main(in: VsOut) -&gt; @location(0) vec4&lt;f32&gt; {
    let sample = textureSample(r_texture, r_sampler, in.tex_coords);
    return sample * in.color;
}
</code></pre>
<p>I don't want to change pipelines to draw the border and I'd like to avoid changing bind groups (because of instancing). How can I conditionally decide in the shader whether to sample from the texture or not?</p>

CC BY-SA 4.0
2023-09-02T10:21:54.527Z
It would be helpful if there were more information on how the outlines are being drawn,  the shader code would be helpful as well.
pmw1234
<shader><texture><sampling>
Conditionally sample from texture in shader
junglie85
# Conditionally sample from texture in shader

(Asked by: username_0 on 2023-09-02)

I'm building a little 2D graphics library in Rust/WGPU as a means to learn graphics programming. I want to draw an outline around a shape and specify the outline's color. All of my shapes have a texture, either explicitly set or a 1x1 white pixel otherwise. When I draw a shape with the default white texture, the outline is as expected but when I do the same with another texture set, it samples the color and sets the outline to it.

Here's an example of the problem: the central rectangle has a white outline as desired, but the top left square has a red outline but it should be white.

[![Example showing correct and incorrect behaviour](https://i.sstatic.net/EWhOT.png)](https://i.sstatic.net/EWhOT.png)

The outline calculation is based on [this answer](https://stackoverflow.com/questions/68973103/how-to-create-outline?noredirect=1&lq=1) and I'm calculating a triangle list so that I can use the same pipeline as the main shape which is also using a triangle list. Relevant aspects of my shaders and data structures are as follows:

```
struct Vertex {
    @location(0) coords: vec2<f32>,
    @location(1) color: vec4<f32>,
    @location(2) tex_coords: vec2<f32>,
}

struct VsOut {
    @builtin(position) position: vec4<f32>,
    @location(0) color: vec4<f32>,
    @location(1) tex_coords: vec2<f32>,
}

@vertex
fn vs_main(vertex: Vertex) -> VsOut {
    ...

    return VsOut(
        vertex.coords,
        vertex.color,
        vertex.tex_coords,
    );
}

@group(1)
@binding(0)
var r_sampler: sampler;
@group(1)
@binding(1)
var r_texture: texture_2d<f32>;

@fragment
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
    let sample = textureSample(r_texture, r_sampler, in.tex_coords);
    return sample * in.color;
}

```

I don't want to change pipelines to draw the border and I'd like to avoid changing bind groups (because of instancing). How can I conditionally decide in the shader whether to sample from the texture or not?

**Tags:** shader, texture, sampling

> **Comments:**
> **username_1**: It would be helpful if there were more information on how the outlines are being drawn,  the shader code would be helpful as well.  

---

## 1 answers

### Answer by username_2 on 2023-09-02. Score: 2 | Accepted answer

> How can I conditionally decide in the shader whether to sample from the texture or not?

Abstractly, this is simple:

* Add a vertex attribute that specifies whether to use the texture or not. Set that attribute appropriately for your outline and your fill (which I understand to be made of separate triangles).
* Pass the attribute on to the fragment shader.
* In the fragment shader,

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

  ```

(The `textureSample()` call is outside of the `if` because it needs to be in “uniform control flow”; this can be avoided but that's another entire topic.)

However, you don't necessarily need to introduce a separate attribute; you can use one that already exists. For example, you could give all the border triangles texture coordinates of `[-1, -1]`, which are clearly not meaningful to the texture (…unless you're using repeating textures). Or, if you never actually want to color-tint a texture, you could designate a particular *color* value to mean “use texture”.

---

Or, instead, you can make sure your textures always have at least one white texel. This is a more appropriate solution if your application uses a texture atlas (unrelated textures arranged inside only one actual texture object) already.

---

Or if you only ever want to draw rectangles, then you can stretch the texture coordinates a bit in your mesh construction and draw a border that way, instead of using extra triangles:

```
if any(in.tex_coords < 0.0) || any(in.tex_coords > 0.0) {
    return sample * in.color;
} else {
    return in.color;
}

```

(Since you're using `wgpu` specifically, I'll mention that you can also use [clamp to border](https://docs.rs/wgpu/0.17.0/wgpu/enum.AddressMode.html#variant.ClampToBorder) to implement this directly in the sampler rather than in shader code. However, that's an extension and is not supported in WebGPU proper.)

> **Comments:**
> **username_0**: I thought about both the flag field in the vertex and the tex coords options. I hadn’t considered the latter would have implications with repeating. I like the texture atlas option but think I’ll go with the vertex attribute for simplicity.  
> **username_0**: I’d be interested to know how to avoid declaring the sampler outside of the if statement - https://computergraphics.stackexchange.com/questions/13729/how-to-avoid-declaring-sampler-outside-of-statement  

---

