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