Woven Pipes Tiling

Shader port from https://www.shadertoy.com/view/DslXD8

Shader code
shader_type canvas_item;

uniform float scale: hint_range(0.0, 1.0) = 1.0;
uniform sampler2D noise_texture;
uniform vec2 offset = vec2(0,0);

// Line segment
float line(vec2 p, vec2 a, vec2 b)
{
    return distance(p, mix(a, b, clamp(dot(p - a, b - a) / dot(b - a, b - a), 0., 1.)));
}

// Two-segment 90-degree angle pipe
float Rline(vec2 p, vec2 a, vec2 b)
{
    if(abs(a.y - .5) > abs(a.x - .5))
    {
        vec2 nx = vec2(clamp(p.x, min(a.x, b.x), max(a.x, b.x)), b.y);
        vec2 ny = vec2(a.x, clamp(p.y, min(a.y, b.y), max(a.y, b.y)));
        return min(distance(p,ny), distance(p, nx));
    }
    else
    {
        vec2 nx = vec2(clamp(p.x, min(a.x, b.x), max(a.x, b.x)), a.y);
        vec2 ny = vec2(b.x, clamp(p.y, min(a.y, b.y), max(a.y, b.y)));
        return min(distance(p,ny), distance(p, nx));
    }
}

// Three-segment S-shaped pipe
float Sline(vec2 p, vec2 a, vec2 b, float t)
{
    vec2 mid = mix(a, b, t);
    if(abs(b.x - a.x) > abs(b.y - a.y))
    {
        vec2 c = vec2(mid.x, a.y), d = vec2(mid.x, b.y);
        return min(min(line(p, a, c), line(p, d, b)), line(p, c, d));
    }
    else
    {
        vec2 c = vec2(a.x, mid.y), d = vec2(b.x, mid.y);
        return min(min(line(p, a, c), line(p, d, b)), line(p, c, vec2(b.x, mid.y)));
    }
}

// Three-segment U-shaped pipe
float Uline(vec2 p, vec2 a, vec2 b)
{
    float m = .2;
    if(abs(b.x - a.x) > abs(b.y - a.y))
    {
        vec2 c = vec2(a.x, a.y + sign(b.y - .5) * m), d = vec2(b.x, a.y + sign(b.y - .5) * m);
        return min(min(line(p, a, c), line(p, c, d)), line(p, d, b));
    }
    else
    {
        vec2 c = vec2(a.x + sign(b.x - .5) * m, a.y), d = vec2(a.x + sign(b.x - .5) * m, b.y);
        return min(min(line(p, a, c), line(p, c, d)), line(p, d, b));
    }
}

float rand(vec2 p)
{
    p = floor(vec2(p.x + p.y, p.x - p.y));
//    float x = texelFetch(noise_texture, ivec2(p) & 255, 0).r * 0.45 + .5;
    float x = texelFetch(noise_texture, ivec2(p), 0).r * 0.45 + .5;
    return floor(x * 4.) / 4.;
}

// Based on https://www.shadertoy.com/view/llGSzw
uint hash1( uint n ) 
{
    // integer hash copied from Hugo Elias
	n = (n << 13U) ^ n;
    n = n * (n * n * 15731U + 789221U) + 1376312589U;
    return n;
}

vec3 draw(vec3 b, float d, float f)
{
    d /= 1.6;
    f /= 1.6 * 2.0;

    // A simple colour ramp for the pipes
    vec3 c0 = mix(vec3(1), vec3(.6, .1, .05), .5 + .5 * smoothstep(0.004, 0.009, d + .001)) +
            vec3(.6, .2, .05) * smoothstep(0.0, 0.02, abs(d - 0.03))+
            vec3(1, .5, .5) * (1. - smoothstep(0.0, 0.02, d)) * .1;
    
    // Shadowed background
    vec3 c1 = b * smoothstep(0.04, .16, d);

    float n0 = 0.05;

    return mix(c0, c1, smoothstep(n0 - f, n0 + f, d));
}

void fragment()
{
    vec2 uv = FRAGCOORD.xy / (scale / SCREEN_PIXEL_SIZE.y);
    uv = uv.xy * 6. + offset.xy / scale / SCREEN_PIXEL_SIZE.xy * 15.;

    // Background pattern
    vec3 col = mix(vec3(.25), vec3(.05, .1, .2) +
                vec3(.02) * step(.5, fract((uv.x + uv.y) * 10.1)), .55);
                
    float m = .1;
    vec2 p = fract(uv);
    vec2 q = floor(uv);
    
    // Eight points around the perimeter of the current square cell
    vec2 c[8] = vec2[8](vec2(mix(m, .5, rand(q + vec2(.5, 0))), 0.),
                        vec2(mix(.5, 1. - m, rand(q + vec2(.5, 0))), 0.),
                        vec2(1., mix(m, .5,rand(q + vec2(1., .5)))),
                        vec2(1., mix(.5, 1. - m, rand(q + vec2(1., .5)))),
                        vec2(mix(m, .5, rand(q + vec2(.5, 1))), 1.),
                        vec2(mix(.5, 1. - m, rand(q + vec2(.5, 1))), 1.),
                        vec2(0., mix(m,.5, rand(q + vec2(0, .5)))),
                        vec2(0., mix(.5, 1. - m, rand(q + vec2(0, .5)))));

    // Shuffle the indices by using the Fisher-Yates algorithm
    // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

    uint inds[8];
    
	inds[0] = 0U;

    uint seed = uint(q.x + q.y * 8192.)*319U;
    for(uint j = 1U; j < 8U; ++j)
    {
        seed = hash1(seed);
        uint k = seed % j;
        uint temp = inds[k];
        inds[k] = j;
        inds[j] = temp;
    }
    
    // Try to avoid creating small rings or terminations
    for(uint j = 0U; j < 8U; j += 2U)
    {
        if(inds[j] / 2U == inds[j + 1U] / 2U)
        {
            uint temp = inds[(j + 3U) % 8U];
            inds[(j + 3U) % 8U] = inds[j];
            inds[j] = temp;
        }
    }
    
    float f = max(length(dFdx(uv)), length(dFdy(uv)));
    
    // Draw pipes connecting pairs of points on the cell perimeter
    for(uint j = 0U; j < 8U; j += 2U)
    {
        uint ia = inds[j];
        uint ib = inds[j + 1U];
        
        if(ia / 2U == ib / 2U)
        {
            // The points are on the same side of the cell
            col.rgb = draw(col.rgb, Uline(p, c[ia], c[ib]), f);
        }
        else if(abs(float(ia / 2U) - float(ib / 2U)) == 2.)
        {
            // The points are on opposite sides of the cell
//            float t = floor(texelFetch(noise_texture, ivec2(p + 30.) & 255, 0).r * 11.) / 11.;
            float t = floor(texelFetch(noise_texture, ivec2(p + 30.), 0).r * 11.) / 11.;
            col.rgb = draw(col.rgb, Sline(p, c[ia], c[ib], mix(.1, .9, t)), f);
        }
        else
        {
            // The points are on perpendicular sides of the cell
            col.rgb = draw(col.rgb, Rline(p, c[ia], c[ib]), f);
        }
    }

    COLOR = vec4(pow(max(col, 0.), vec3(1. / 2.2)), 1.);
}
Tags
#shadertoy
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from BlueOctopus

math – rotate uv

Related shaders

Hexagonal tiling + cog wheels

Tiling Dot Background

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments