Back to shaders

Shader test bench

Worley Cells.fs

vidvox-isf-files utility glsl runnable fragment MIT
Source
runnable fragment

Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.

Code

uniform float density;
uniform float jitter;
uniform float speed;
uniform int metric;
uniform int colorMode;
uniform vec4 cellColor1;
uniform vec4 cellColor2;
uniform float borderWidth;
uniform vec4 borderColor;
uniform float seed;
precision mediump float;
/*{
    "CATEGORIES": [
        "Generator", "Geometry", "Noise"
    ],
    "CREDIT": "by VIDVOX",
    "DESCRIPTION": "Worley/Voronoi cellular noise generator.",
    "ISFVSN": "2",
    "INPUTS": [
        {
            "DEFAULT": 10.0,
            "LABEL": "Density",
            "MAX": 50.0,
            "MIN": 1.0,
            "NAME": "density",
            "TYPE": "float"
        },
        {
            "DEFAULT": 1.0,
            "LABEL": "Jitter",
            "MAX": 1.0,
            "MIN": 0.0,
            "NAME": "jitter",
            "TYPE": "float"
        },
        {
            "DEFAULT": 0.0,
            "LABEL": "Speed",
            "MAX": 2.0,
            "MIN": 0.0,
            "NAME": "speed",
            "TYPE": "float"
        },
        {
            "DEFAULT": 0,
            "LABEL": "Distance Metric",
            "LABELS": [
                "Euclidean (Round)",
                "Manhattan (Diamond)",
                "Chebyshev (Square)"
            ],
            "NAME": "metric",
            "TYPE": "long",
            "VALUES": [
                0,
                1,
                2
            ]
        },
        {
            "DEFAULT": 0,
            "LABEL": "Color Mode",
            "LABELS": [
                "Random Per Cell",
                "Distance Gradient",
                "F1 Grayscale"
            ],
            "NAME": "colorMode",
            "TYPE": "long",
            "VALUES": [
                0,
                1,
                2
            ]
        },
        {
            "DEFAULT": [
                0.1,
                0.4,
                0.8,
                1.0
            ],
            "LABEL": "Color 1",
            "NAME": "cellColor1",
            "TYPE": "color"
        },
        {
            "DEFAULT": [
                0.9,
                0.7,
                0.2,
                1.0
            ],
            "LABEL": "Color 2",
            "NAME": "cellColor2",
            "TYPE": "color"
        },
        {
            "DEFAULT": 0.05,
            "LABEL": "Border Width",
            "MAX": 0.5,
            "MIN": 0.0,
            "NAME": "borderWidth",
            "TYPE": "float"
        },
        {
            "DEFAULT": [
                0.0,
                0.0,
                0.0,
                1.0
            ],
            "LABEL": "Border Color",
            "NAME": "borderColor",
            "TYPE": "color"
        },
        {
            "DEFAULT": 0.0,
            "LABEL": "Seed",
            "MAX": 100.0,
            "MIN": 0.0,
            "NAME": "seed",
            "TYPE": "float"
        }
    ]
}*/


#define TAU 6.28318530717958647692


//	2D hash returning (0..1, 0..1) per input vec2. Standard 'iq' sin-based hash.
vec2 hash2(vec2 p)	{
	p = vec2(dot(p, vec2(127.1, 311.7)),
			 dot(p, vec2(269.5, 183.3)));
	return fract(sin(p) * 43758.5453);
}

//	1D hash for per-cell color lookup.
float hash1(vec2 p)	{
	return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

//	Distance under the selected metric.
//	0 = Euclidean (round cells)
//	1 = Manhattan (diamond cells)
//	2 = Chebyshev (square cells)
float distMetric(vec2 d)	{
	if (metric == 1) return abs(d.x) + abs(d.y);
	if (metric == 2) return max(abs(d.x), abs(d.y));
	return length(d);
}


void main()	{
	vec2 uv = (gl_FragCoord.xy / u_resolution.xy);
	
	//	Cell-space coordinate, aspect-corrected so cells render visually square
	//	regardless of frame aspect.
	float aspect = u_resolution.x / u_resolution.y;
	vec2  cellSpace = vec2(uv.x * aspect, uv.y) * density;
	vec2  cell = floor(cellSpace);
	
	//	Track the closest (F1) and second-closest (F2) feature-point distances.
	//	Their difference (F2 - F1) is the standard Worley edge function.
	float F1 = 1e10;
	float F2 = 1e10;
	vec2  winnerCell = cell;
	
	//	3x3 neighborhood scan — sufficient with jitter clamped to <=1.
	for (int dy = -1; dy <= 1; ++dy)	{
		for (int dx = -1; dx <= 1; ++dx)	{
			vec2 neighbor = cell + vec2(float(dx), float(dy));
			vec2 h = hash2(neighbor + seed);
	
			//	Deterministic jittered base position within the neighbor cell.
			vec2 basePos = vec2(0.5) + (h - 0.5) * jitter;
	
			//	Optional sinusoidal drift when speed > 0. Each feature point
			//	gets its own phase via the cell hash, so the field evolves
			//	continuously without any sudden re-shuffling.
			vec2 anim = speed * 0.3 * vec2(
				sin(u_time * speed + h.x * TAU),
				cos(u_time * speed + h.y * TAU)
			);
	
			vec2  fpPos = neighbor + basePos + anim;
			float d = distMetric(cellSpace - fpPos);
	
			if (d < F1)	{
				F2 = F1;
				F1 = d;
				winnerCell = neighbor;
			}
			else if (d < F2)	{
				F2 = d;
			}
		}
	}
	
	//	===== Cell fill =====
	vec3 fill;
	if (colorMode == 0)	{
		//	Random Per Cell — hash the winning cell's coordinate to pick a
		//	color mix factor between Color 1 and Color 2.
		float cellHash = hash1(winnerCell + seed + 17.3);
		fill = mix(cellColor1.rgb, cellColor2.rgb, cellHash);
	}
	else if (colorMode == 1)	{
		//	Distance Gradient — Color 1 at the feature point, Color 2 at the edge.
		float t = clamp(F1, 0.0, 1.0);
		fill = mix(cellColor1.rgb, cellColor2.rgb, t);
	}
	else	{
		//	F1 Grayscale — raw nearest-distance value, useful as a luminance source.
		fill = vec3(clamp(F1, 0.0, 1.0));
	}
	
	//	===== Cell border =====
	//	(F2 - F1) is the distance to the boundary between this cell and its
	//	nearest neighbor. Small = near border, large = deep in the cell.
	float edgeDist = F2 - F1;
	float borderAmount = 1.0 - smoothstep(borderWidth * 0.5, borderWidth, edgeDist);
	
	vec3 finalColor = mix(fill, borderColor.rgb, borderAmount * borderColor.a);
	
	gl_FragColor = vec4(finalColor, 1.0);
}