megalith/assets/materials/shaders/fractal_steel.gdshader
2025-10-03 15:06:48 -06:00

103 lines
1.9 KiB
Plaintext

// Voronoi fractal steel material
// Based on https://www.shadertoy.com/view/4sl3Dr
shader_type spatial;
uniform vec2 uv_scale = vec2(1.0);
uniform float edge_sharpness = 0.25;
uniform vec3 base_color: source_color = vec3(1.0);
uniform int octaves = 3;
uniform float octave_dropoff = 0.7;
uniform float octave_scale = 3.0;
uniform float metallic = 1.0;
uniform float roughness_scale = 1.0;
uniform float bump_strength = 1.0;
// 1D random numbers
float rand(float n)
{
return fract(sin(n) * 43758.5453123374897);
}
// 2D random numbers
vec2 rand2(vec2 p)
{
return fract(vec2(sin(p.x * 591.32 + p.y * 154.077), cos(p.x * 391.32 + p.y * 49.077)));
}
// 1D noise
float noise1(float p)
{
float fl = floor(p);
float fc = fract(p);
return mix(rand(fl), rand(fl + 1.0), fc);
}
// voronoi distance noise, based on iq's articles
float voronoi(vec2 x)
{
vec2 p = floor(x);
vec2 f = fract(x);
vec2 res = vec2(8.0);
for(float j = -1.0; j <= 1.0; j ++)
{
for(float i = -1.0; i <= 1.0; i ++)
{
vec2 b = vec2(i, j);
vec2 r = vec2(b) - f + rand2(p + b);
// chebyshev distance, one of many ways to do this
float d = max(abs(r.x), abs(r.y));
if(d < res.x)
{
res.y = res.x;
res.x = d;
}
else if(d < res.y)
{
res.y = d;
}
}
}
return res.y - res.x;
}
void fragment() {
vec2 uv = (UV - 0.5) * 2.0 * uv_scale;
float value = 0.0;
// Octaves
float a = 0.6;
float f = 1.0;
for(int i = 0; i < octaves; i++) {
float v1 = voronoi(uv * f + 5.0);
float v2 = 0.0;
// Sharp edges
v1 = 1.0 - smoothstep(0.0, edge_sharpness, v1);
// noise as intensity map
v2 = a * (noise1(v1 * 5.5 + 0.1));
value += v2;
f *= octave_scale;
a *= octave_dropoff;
}
// Use value as bump map
float dZ = 1.0 / bump_strength;
vec3 nmap = normalize(vec3(dFdx(value), dFdy(value), dZ));
NORMAL_MAP = nmap / 2.0 + 1.0;
ALBEDO = value * base_color;
ROUGHNESS = value * roughness_scale;
METALLIC = metallic;
}