Curated Creative Algorithms by Raphael de Courville and Taru Muhonen

Bookmarked for your convenience. Previously there was discussion about converting a Shadertoy metaballs shader to processing. Inspired by the creative algorithms site I converted another metaball example to JRubyArt.

# from shadertoy example by Edan Kwan
# https://www.shadertoy.com/view/3sySRK

attr_reader :previous_time, :wrapper, :start

def settings
  size(640, 360, P2D)
end

def setup
  sketch_title 'Metaballs Two'
  @previous_time = 0.0
  @wrapper = load_shader(data_path('metaballs_two.glsl'))
  # Assume the dimension of the window will not change over time
  wrapper.set('iResolution', width.to_f, height.to_f, 0.0)
  @start = java.lang.System.current_time_millis
end

def playback_time_seconds
  (java.lang.System.current_time_millis - start) / 1000.0
end

def render_time
  playback_time_seconds - previous_time
end

def draw
  wrapper.set('iTime', playback_time_seconds)
  @previous_time = playback_time_seconds
  # Apply the specified shader to any geometry drawn from this point
  shader(wrapper)
  # Draw the output of the shader onto a rectangle that covers the whole viewport.
  rect(0, 0, width, height)
end

The shader

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

uniform vec3      iResolution;           // viewport resolution (in pixels)
uniform float     iTime;                 // shader playback time (in seconds)


float opSmoothUnion( float d1, float d2, float k )
{
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return mix( d2, d1, h ) - k*h*(1.0-h);
}

float sdSphere( vec3 p, float s )
{
  return length(p)-s;
}

float map(vec3 p)
{
	float d = 2.0;
	for (int i = 0; i < 16; i++) {
		float fi = float(i);
		float time = iTime * (fract(fi * 412.531 + 0.513) - 0.5) * 2.0;
		d = opSmoothUnion(
            sdSphere(p + sin(time + fi * vec3(52.5126, 64.62744, 632.25)) * vec3(2.0, 2.0, 0.8), mix(0.5, 1.0, fract(fi * 412.531 + 0.5124))),
			d,
			0.4
		);
	}
	return d;
}

vec3 calcNormal( in vec3 p )
{
    const float h = 1e-5; // or some other value
    const vec2 k = vec2(1,-1);
    return normalize( k.xyy*map( p + k.xyy*h ) +
                      k.yyx*map( p + k.yyx*h ) +
                      k.yxy*map( p + k.yxy*h ) +
                      k.xxx*map( p + k.xxx*h ) );
}

void main(void)
{
  vec2 uv = gl_FragCoord.xy/iResolution.xy;

    // screen size is 6m x 6m
	vec3 rayOri = vec3((uv - 0.5) * vec2(iResolution.x/iResolution.y, 1.0) * 6.0, 3.0);
	vec3 rayDir = vec3(0.0, 0.0, -1.0);

	float depth = 0.0;
	vec3 p;

	for(int i = 0; i < 64; i++) {
	  p = rayOri + rayDir * depth;
		float dist = map(p);
        depth += dist;
		if (dist < 1e-6) {
			break;
		}
	}

  depth = min(6.0, depth);
	vec3 n = calcNormal(p);
    float b = max(0.0, dot(n, vec3(0.577)));
    vec3 col = (0.5 + 0.5 * cos((b + iTime * 3.0) + uv.xyx * 2.0 + vec3(0,2,4))) * (0.85 + b * 0.35);
    col *= exp( -depth * 0.15 );

    // maximum thickness is 2m in alpha channel
    gl_FragColor = vec4(col, 1.0 - (depth - 0.5) / 2.0);
}

metaballs

1 Like

Another algorithm I translate for JRubyArt was Worley Noise after Dan Schiffman example:-

# Worley Noise
# After
# The Coding Train / Daniel Shiffman
attr_reader :points

NPOINTS = 20

def settings
  size(400, 400)
end

def setup
  sketch_title 'Worley Noise'
  @points = (0..NPOINTS).map { Vec3D.new(rand(width), rand(height), rand(width)) }
end

def draw
  load_pixels
  grid(width, height) do |x, y|
    distances = []
    NPOINTS.times do |i|
      v = points[i]
      z = frame_count % width
      distances << dist(x, y, z, v.x, v.y, v.z)
    end
    sorted = distances.sort!
    r = map1d(sorted[0], 0..150, 0..255)
    g = map1d(sorted[1], 0..50, 255..0)
    b = map1d(sorted[2], 0..200, 255..0)
    pixels[x + y * width] = color(r, g, b)
  end
  update_pixels
end

Somewhat more condensed than vanilla processing or p5.js