1
$\begingroup$

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

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

$\endgroup$
1
  • $\begingroup$ It would be helpful if there were more information on how the outlines are being drawn, the shader code would be helpful as well. $\endgroup$
    – pmw1234
    Sep 2, 2023 at 10:21

1 Answer 1

2
$\begingroup$

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

$\endgroup$
2
  • $\begingroup$ 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. $\endgroup$
    – junglie85
    Sep 3, 2023 at 16:33
  • $\begingroup$ I’d be interested to know how to avoid declaring the sampler outside of the if statement - computergraphics.stackexchange.com/questions/13729/… $\endgroup$
    – junglie85
    Sep 23, 2023 at 19:27

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.