I want to create a shader outline for a sprite and for that I want to expand the region around the sprite so there is more white space.

I know there is a region option for Sprite2D but that one repeats the border pixels
godot region

I tried to play around the rect values and the texture settings (both Filter and Repeat) but I could never just get rid of the repeated pixels. Am I missing something?

  • xyz replied to this.
  • izNoob You need to check UV. If UV is in range 0-1, the pixel is "inside" the image, otherwise it's the pixel you want to "hide" by setting its alpha to 0.

    if(UV.x < 0.0 || UV.x > 1.0 || UV.y < 0.0 || UV.y > 1.0){
    	COLOR.a = 0.0;
    }

    However we don't want to do it like that in a shader because it involves code branching. And this ties in with your previous question 😉
    Instead we do this:

    COLOR.a = step(UV.x, 1.0) * step(0.0, UV.x) * step(UV.y, 1.0) * step(0.0, UV.y);

    izNoob You need to check UV. If UV is in range 0-1, the pixel is "inside" the image, otherwise it's the pixel you want to "hide" by setting its alpha to 0.

    if(UV.x < 0.0 || UV.x > 1.0 || UV.y < 0.0 || UV.y > 1.0){
    	COLOR.a = 0.0;
    }

    However we don't want to do it like that in a shader because it involves code branching. And this ties in with your previous question 😉
    Instead we do this:

    COLOR.a = step(UV.x, 1.0) * step(0.0, UV.x) * step(UV.y, 1.0) * step(0.0, UV.y);

    You can do this in a shader with two function calls. I think this is the most optimized.

    const vec2 bottomLeft = vec2(0.0, 0.0);
    const vec2 topRight = vec2(1.0, 1.0);
    
    float withinTexture(vec2 uv) {
        vec2 within = step(bottomLeft, uv) - step(topRight, uv);
        return within.x * within.y;
    }

    1.0 is true and 0.0 is false.

    • xyz replied to this.

      Thanks for the answers!

      Using the alpha values of the current pixel is a really good solution. Although if someone else is reading this: I think the easiest solution is to use an AtlasTexture and give it a single texture. Then you don't even need to code anything.

      • xyz replied to this.

        izNoob Not a good idea to pull in all the atlas overhead just to mask out the texture. If you're doing the outline around the sprite you'll have to use a shader anyway.

        cybereality
        Just for fun, here's the least expensive way I could think of:

        COLOR.a = 1.0 - length(floor(UV));

        On the second thought maybe it's not least expensive but it certainly is the shortest.
        Here's the one that might actually be though:

        COLOR.a = float(!any(bvec2(floor(UV))));

        EDIT: Missed the obvious one. This is probably the fastest way:

        COLOR.a = 1.0 - dot(floor(UV), floor(UV));

        Double call to floor will be optimized by the compiler. I didn't want to use a temp variable so it stays an oneliner.
        Sorry, got a little bit obsessed 😃.

        To avoid confusion - all of the methods suggested in this thread are ok to use.