Previously I presented a Newton fractal sketch using ruby Complex numbers. Then I explored shadertoy and converted a sketch to Picrate. This gave me the confidence to develop my own variant:-
Here is the PiCrate code:-
require 'picrate'
class NewtonFractal < Processing::App
attr_reader :last_mouse_position, :mouse_click_state, :mouse_dragged
attr_reader :newton
def settings
size(600, 600, P2D)
end
def setup
sketch_title 'Newton Fractal Shader'
@mouse_dragged = false
@mouse_click_state = 0.0
# Load the shader file from the "data" folder
@newton = load_shader(data_path('my_newton.glsl'))
# Assume the dimension of the window will not change over time
newton.set('resolution', width.to_f, height.to_f)
@last_mouse_position = Vec2D.new(mouse_x.to_f, mouse_y.to_f)
end
def draw
# mouse pixel coords. xy: current (if MLB down), zw: click
if mouse_pressed?
@last_mouse_position = Vec2D.new(mouse_x.to_f, mouse_y.to_f)
@mouse_click_state = 1.0
else
@mouse_click_state = 0.0
end
newton.set('iMouse', last_mouse_position.x, last_mouse_position.y, mouse_click_state, mouse_click_state)
# Apply the specified shader to any geometry drawn from this point
shader(newton)
# Draw the output of the shader onto a rectangle that covers the whole viewport.
rect(0, 0, width, height)
end
end
NewtonFractal.new
And my shader, note this is no longer a wrapped shadertoy shader, and is suitable for direct use in other processing variants (try it for yourself in processing.py or vanilla processing suggestions for improvement welcome):-
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
#define ITER 12
#define NEWTON 3 // 2,3,4,5
uniform vec2 resolution; // viewport resolution (in pixels)
uniform int iFrame; // shader playback frame
uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
//Complex Math:
vec2 cexp(in vec2 z) { return vec2(exp(z.x)*cos(z.y),exp(z.x)*sin(z.y)); }
vec2 clog(in vec2 z) { return vec2(log(length(z)), atan(z.y, z.x)); }
vec2 cinv(in vec2 a) { return vec2(a.x, -a.y) / dot(a, a); }
vec2 cmul(in vec2 a, in vec2 b) { return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); }
vec2 cdiv(in vec2 a, in vec2 b) { return cmul(a, cinv(b)); }
vec2 cpow(in vec2 a, in vec2 b){ return cexp( cmul(b,clog(a))); }
//---------------------------------------------------------
vec2 newton( in vec2 z )
{
for (int i = 0; i < ITER; i++)
{
vec2 z2 = cpow(z, vec2(2, 0));
vec2 z3 = cpow(z, vec2(3, 0));
// vec2 z4 = cpow(z, vec2(4, 0));
// vec2 z5 = cpow(z, vec2(5, 0));
// vec2 z6 = cpow(z, vec2(6, 0));
// vec2 z7 = cpow(z, vec2(7, 0));
// vec2 z8 = cpow(z, vec2(8, 0));
//---> change z calculation by uncomment different lines
#if NEWTON==2
z -= cdiv(z3 - 1.0, 3.0 * z2); // original: z^3 - 1 / ( 3*z^2)
#elif NEWTON==3
z -= cdiv(z3 - 0.5+0.05*iMouse.y, (0.5+0.01*iMouse.x) * z2); // z^3 - my / (mx*z^2)
#elif NEWTON==4
z -= cdiv(z4 - 0.5+0.05*iMouse.y, (0.5+0.01*iMouse.x) * z3); // z^4 - my / (mx*z^3)
#elif NEWTON==5
z -= cdiv(z5 - 0.5+0.05*iMouse.y, (0.5+0.1*iMouse.x) * z4); // z^5 - my / (mx*z^4)
#elif NEWTON==8
z -= cdiv(z8 - 0.5+0.05*iMouse.y, (0.5+0.1*iMouse.x) * z7); // z^5 - my / (mx*z^4)
#endif
}
return z;
}
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb); // cubic smoothing
return c.z * mix( vec3(1.0), rgb, c.y);
}
vec3 getColor(in float t){
vec3 col = vec3(t, t, t);
return hsb2rgb(col);
}
//---------------------------------------------------------
void main(void)
{
vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
uv.x *= resolution.x / resolution.y;
vec2 z = newton(uv);
vec3 col = getColor(length(z));
gl_FragColor = vec4(col, 1.0);
}
Using HSB color makes all the difference.