// Original code is MIT licensed by Cory Petkovsek // https://github.com/TokisanGames/godot-fractal-art shader_type canvas_item; uniform bool smoothing = true; uniform bool use_color_gradient = false; uniform vec4 gradient_start : source_color = vec4(0.18, 0., 0.45, 1.); uniform vec4 gradient_end : source_color = vec4(1., 1., 0., 1.); uniform vec4 gradient_accent : source_color = vec4(1., 1., 0., 1.); uniform float accent_position = 0.65; uniform float accent_width = 0.34; uniform float red_frequency = 1.6615; uniform float green_frequency = 1.246; uniform float blue_frequency = 0.831; uniform float red_phase = 6.3; uniform float green_phase = 6.3; uniform float blue_phase = 6.3; uniform highp float scale = 0.5; uniform highp vec2 position = vec2(0, 0); uniform float aspect_ratio = 1.33333; uniform highp vec2 seed = vec2(-0.794084, 0.136444); uniform int power = 2; uniform float iterations = 50.0; uniform float escape = 4.0; // Convert from rectangular to polar highp vec2 c_pol(highp vec2 c) { highp float radius = length(c); highp float theta = atan(c.y, c.x); return vec2(radius, theta); } // Convert from rectangular to Polar highp vec2 c_rec(highp vec2 c) { highp float radius = abs(c.x); highp float theta = c.y; highp float a = radius * cos(theta); highp float b = radius * sin(theta); return vec2(a, b); } // Calculate base ^ exponent highp vec2 c_pow(highp vec2 base, highp float ex) { highp vec2 b = c_pol(base); return c_rec(vec2(pow(b.x, ex), b.y * ex)); } vec4 alpha_blend(vec4 top, vec4 bot) { return vec4(top.r * top.a + bot.r * bot.a * (1. - top.a), top.g * top.a + bot.g * bot.a * (1. - top.a), top.b * top.a + bot.b * bot.a * (1. - top.a), 1.0); } vec4 getColor(float i) { if (use_color_gradient) { // Create gradient vec4 ramp = mix(gradient_start, gradient_end, i); // gaussian formula ae^( -(x-b)^2 / 2c^2 ) // a = curve's peak = 1.0 // b = center of peak position // c = standard deviation which controls width of bell float gaussianx = exp(-((i - accent_position) * (i - accent_position)) / (2. * accent_width * accent_width)); vec4 accent = vec4(gradient_accent.r * gaussianx, gradient_accent.g * gaussianx, gradient_accent.b * gaussianx, gaussianx); return alpha_blend(accent, ramp); } else { /* Sin/Cos creates a smooth wave between 1 and -1, offset from each other. * You can have 4 evenly distributed offsets from sin, cos, -sin, -cos * (sin(x) +1) / 2 -> changes the wave to go between 0 and 1 with the same frequency, +2../4 will go from 0.5 and 1.0 * Calculate full cycles with: sin(cycles*6.5*i) or (sin(cycles*5.4*i)+1.0)/2.0 or (cos(cycles*3.8*i)+1.0)/2.0 */ return vec4((sin(red_frequency * 6.5 * i + red_phase) + 1.0) / 2.0, (sin(green_frequency * 6.5 * i + green_phase) + 1.0) / 2.0, (sin(blue_frequency * 6.5 * i + blue_phase) + 1.0) / 2.0, 1.); } } vec2 random(vec2 uv) { return vec2(fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123)); } vec2 voronoi(vec2 uv, float columns, float rows) { vec2 index_uv = floor(vec2(uv.x * columns, uv.y * rows)); vec2 fract_uv = fract(vec2(uv.x * columns, uv.y * rows)); float minimum_dist = 1.0; vec2 minimum_point; for (int y= -1; y <= 1; y++) { for (int x= -1; x <= 1; x++) { vec2 neighbor = vec2(float(x),float(y)); vec2 point = random(index_uv + neighbor); vec2 diff = neighbor + point - fract_uv; float dist = length(diff); if(dist < minimum_dist) { minimum_dist = dist; minimum_point = point; } } } return minimum_point; } void fragment() { float i = 0.0; highp vec2 c = seed; highp vec2 z; z.x = aspect_ratio * (UV.x - 0.5) / scale - position.x; z.y = (UV.y - 0.5) / scale + position.y; if (power == 2) { while (i < iterations && length(z) <= escape) { z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c; // 20fps i++; } } else { while (i < iterations && length(z) <= escape) { z = c_pow(z, float(power)) + c; // 6-7fps i++; } } if (i >= iterations) { float r = voronoi(UV + seed, 10.0, 5.0).r; COLOR = getColor(r); } else { if (smoothing && power > 1) { i -= log(log(length(z))) / log(float(power)); } COLOR = getColor(i / iterations); } }