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

The outline calculation is based on this answer 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,
    @location(1) color: vec4,
    @location(2) tex_coords: vec2,
}

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

@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;

@fragment
fn fs_main(in: VsOut) -> @location(0) vec4 {
    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 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  

---

