Search code examples
glslfragment-shadergodotgdscript

Godot shader: The borders of my Texture is stretching to fit into a plane mesh


I got a simple plane mesh with a shader attached to it.

shader_type spatial;  
uniform sampler2D texture_0: repeat_disable; 
uniform sampler2D texture_1: repeat_disable; 
uniform sampler2D texture_2: repeat_disable; 
uniform sampler2D texture_3: repeat_disable;  

void fragment(){     
    ALBEDO = texture(texture_0,UV*2).rgb; 

In that code I just multiplied the UV by 2 to divide everything into 4 pieces Then I added the repeat_disable hint to the textures to prevent the textures to repeat when resized.

My problem is now that the textures are stretching at their borders to fill the empty space vertically and horizontally.

Stretching texture borders

I need to assign the 4 textures to the plane mesh in row, they should not overlap each other, Cant really tell how to solve this one now If anyone knows something, id be pleased ;c


Solution

  • Ok, you need variables that you can use to discriminate which texture you will use. To be more specific, four variables (one per texture) which will be 1 where the texture goes, and 0 elsewhere.

    We will get there. I'm taking you step by step, so this approach can be adapted to other situations and you have understanding of what is going on.


    Let us start by… All white!

    void fragment()
    {
        ALBEDO = vec3(1.0);
    }
    

    OK, not super useful. Let us split in two, horizontally. An easy way to do that is with the step function:

    void fragment()
    {
        ALBEDO = vec3(step(0.5, UV.x));
    }
    

    That will be black on the left (low x) and white on the right (hi x).

    By the way, if you are not sure about the orientation, output the UV:

    void fragment()
    {
        ALBEDO = vec3(UV, 0.0);
    }
    

    Alright, if we wanted to flip a variable t, we can do 1.0 - t. So this is white on the left (low x) and black on the right (hi x):

    void fragment()
    {
        ALBEDO = vec3(1.0 - step(UV.x, 0.5));
    }
    

    By the way, flipping the parameters of step archives the same result:

    void fragment()
    {
        ALBEDO = vec3(step(0.5, UV.x));
    }
    

    And if we wanted to do it vertically, we can work with y:

    void fragment()
    {
        ALBEDO = vec3(step(UV.y, 0.5));
    }
    

    Now, to get a quadrant, we can intersect/and these. I mean, multiply them. For example:

    void fragment()
    {
        ALBEDO = vec3(step(UV.y, 0.5) * step(UV.x, 0.5));
    }
    

    So, your quadrants look like this:

        float q0 = step(UV.y, 0.5) * step(0.5, UV.x);
        float q1 = step(UV.y, 0.5) * step(UV.x, 0.5);
        float q2 = step(0.5, UV.y) * step(UV.x, 0.5);
        float q3 = step(0.5, UV.y) * step(0.5, UV.x);
    

    This might not be the order you want.


    Now you can either leave the texture repeat, or we need to compute the appropriate UV. I'll start with the version that needs repeat on.

    We can intersect the textures with the values we computed, so they only come out where we want them. I mean, we can use these values to mask the textures with and. I mean, we multiply. Where a variable is 0 (black) you will not get anything from the texture, and where it is 1 (white) you get the texture.

    That is something like this:

        vec3 t0 = q0 * texture(texture_0, UV * 2.0).rgb;
        vec3 t1 = q1 * texture(texture_1, UV * 2.0).rgb;
        vec3 t2 = q2 * texture(texture_2, UV * 2.0).rgb;
        vec3 t3 = q3 * texture(texture_3, UV * 2.0).rgb;
    

    And we add them:

    ALBEDO = t0 + t1 + t2 + t3;
    

    On the other hand, if the textures don't repeat, we need to adjust the UVs. Why? Well, because the valid range is from 0.0 to 1.0, but UV * 2.0 goes from 0.0 to 2.0...

    You can output that to get an idea:

    void fragment()
    {
        ALBEDO = vec3(UV * 2.0, 0.0);
    }
    

    I'll write that like this, if you don't mind:

    void fragment()
    {
        ALBEDO = vec3(vec2(UV.x, UV.y) * 2.0, 0.0);
    }
    

    Which is the same. But since I'll be working on the axis separately, it helps me.

    With the UV adjusted, it looks like this:

        vec3 t0 = q0 * texture(texture_0, vec2(UV.x - 0.5, UV.y) * 2.0).rgb;
        vec3 t1 = q1 * texture(texture_1, vec2(UV.x, UV.y) * 2.0).rgb;
        vec3 t2 = q2 * texture(texture_2, vec2(UV.x, UV.y - 0.5) * 2.0).rgb;
        vec3 t3 = q3 * texture(texture_3, vec2(UV.x - 0.5, UV.y - 0.5) * 2.0).rgb;
    

    This might not be the order you want.

    And again, add them:

    ALBEDO = t0 + t1 + t2 + t3;
    

    You can output the adjusted UVs there to have a better idea of what is going on.


    Please notice that what we are doing is technically a weighted sum of the textures. Except it is done in such way that only one of them appears at any location (only one has a factor of 1 and the others have a factor of 0). The same approach can be used to make other patterns or textures blend by using other computations for the factors (and once you are beyond using only black and white, you can also apply easing functions). You might even pick the factors by reading yet another texture.

    By the way, I told you and/intersection (a * b) and not/complement (1.0 - t). For black and white masks, this is or/union: a + b - a * b. However, if you know there is no overlap you can ignore the last part so it is just addition. So when we add the textures, is an union, you can think of it in term of Venn diagrams.