Previously I posted examples of my shadertoy2jruby_art tool in action. But sometimes that’s not quite enough for some ShaderToy sketches like the example of the week by Leon Denise which I split into two shader files as follows
attr_reader :last_mouse_position, :mouse_click_state, :mouse_dragged
attr_reader :previous_time, :wrapper, :start
# uniform iChannelTime[4] # channel playback time (in seconds)
# uniform vec3 iChannelResolution[4] # channel resolution (in pixels)
# uniform samplerXX iChannel0..3 # input channel. XX = 2D/Cube
def settings
size(640, 360, P2D)
end
def setup
sketch_title 'Taste of noise 7'
@previous_time = 0.0
@mouse_dragged = false
@mouse_click_state = 0.0
# Load the shader file from the "data" folder
@wrapper = load_shader(data_path('taste_of_noise7.glsl'))
@image = load_shader(data_path('image.glsl'))
# Assume the dimension of the window will not change over time
wrapper.set('iResolution', width.to_f, height.to_f, 0.0)
@last_mouse_position = Vec2D.new(mouse_x, mouse_y)
@start = java.lang.System.current_time_millis
end
def draw
# shader playback time (in seconds)
current_time = (java.lang.System.current_time_millis - start) / 1000.0
wrapper.set('iTime', current_time)
@image.set('iChannel0', get())
# render time (in seconds)
@previous_time = current_time
# mouse pixel coords. xy: current (if MLB down), zw: click
if mouse_pressed?
@last_mouse_position = Vec2D.new(mouse_x, mouse_y)
@mouse_click_state = 1.0
else
@mouse_click_state = 0.0
end
wrapper.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(wrapper)
# Draw the output of the shader onto a rectangle that covers the whole viewport.
rect(0, 0, width, height)
end
Image File
// taste of noise 7 by leon denise 2021/10/14
// result of experimentation with organic patterns
// using code from Inigo Quilez, David Hoskins and NuSan
// thanks to Fabrice Neyret for code reviews
// licensed under hippie love conspiracy
// return color from pixel coordinate
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
fragColor = texture(iChannel0, fragCoord.xy/iResolution.xy);
}
Shader File
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
// ----------------------
// SHADERTOY UNIFORMS -
// ----------------------
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform float iTime; // shader playback time (in seconds)
uniform float iTimeDelta; // render time (in seconds)
uniform int iFrame; // shader playback frame
uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
uniform vec4 iDate; // (year, month, day, time in seconds)
uniform sampler2D iChannel0;
void mainImage( out vec4 fragColor, in vec2 fragCoord );
void main() {
mainImage(gl_FragColor,gl_FragCoord.xy);
}
// ------------------------------
// SHADERTOY CODE BEGINS HERE -
// ------------------------------
// Dave Hoskins
// https://www.shadertoy.com/view/4djSRW
float hash13(vec3 p3)
{
p3 = fract(p3 * .1031);
p3 += dot(p3, p3.zyx + 31.32);
return fract((p3.x + p3.y) * p3.z);
}
// Inigo Quilez
// https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
float smin( 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 smoothing(float d1, float d2, float k) { return clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 ); }
// rotation matrix
mat2 rot(float a) { return mat2(cos(a),-sin(a),sin(a),cos(a)); }
#define repeat(p,r) (mod(p,r)-r/2.)
// taste of noise 7 by leon denise 2021/10/14
// result of experimentation with organic patterns
// using code from Inigo Quilez, David Hoskins and NuSan
// thanks to Fabrice Neyret for code reviews
// licensed under hippie love conspiracy
// global variable
float material;
float rng;
// sdf
float map (vec3 p)
{
// time stretched with noise
float t = iTime*1. + rng*0.9;
// domain repetition
float grid = 5.;
vec3 cell = floor(p/grid);
p = repeat(p,grid);
// distance from origin
float dp = length(p);
// rotation parameter
vec3 angle = vec3(.1,-.5,.1) + dp*.5 + p*.1 + cell;
// shrink sphere size
float size = sin(rng*3.14);
// stretch sphere
float wave = sin(-dp*1.+t+hash13(cell)*6.28)*.5;
// kaleidoscopic iterated function
const int count = 4;
float a = 1.0;
float scene = 1000.;
float shape = 1000.;
for (int index = 0; index < count; ++index)
{
// fold and translate
p.xz = abs(p.xz)-(.5+wave)*a;
// rotate
p.xz *= rot(angle.y/a);
p.yz *= rot(angle.x/a);
p.yx *= rot(angle.z/a);
// sphere
shape = length(p)-0.2*a*size;
// material blending
material = mix(material, float(index), smoothing(shape, scene, 0.3*a));
// add with a blend
scene = smin(scene, shape, 1.*a);
// falloff transformations
a /= 1.9;
}
return scene;
}
// return color from pixel coordinate
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// reset color
fragColor = vec4(0,0,0,1);
material = 0.0;
// camera coordinates
vec2 uv = (fragCoord.xy - iResolution.xy * 0.5) / iResolution.y;
vec3 eye = vec3(1,1,1.);
vec3 at = vec3(0,0,0);
vec3 z = normalize(at-eye);
vec3 x = normalize(cross(z, vec3(0,1,0)));
vec3 y = cross(x, z);
vec3 ray = normalize(vec3(z + uv.x * x + uv.y * y));
vec3 pos = eye;
// camera control
vec2 M = 6.28*(iMouse.xy-.5);
ray.xz *= rot(M.x), pos.xz *= rot(M.x);
ray.xy *= rot(M.y), pos.xy *= rot(M.y);
// white noise
vec3 seed = vec3(fragCoord.xy, iTime);
rng = hash13(seed);
// raymarch
const float steps = 30.0;
float index;
for (index = steps; index > 0.0; --index)
{
// volume estimation
float dist = map(pos);
if (dist < 0.01)
{
break;
}
// dithering
dist *= 0.9 + .1 * rng;
// raymarch
pos += ray * dist;
}
// ambient occlusion from steps count
float shade = index/steps;
// compute normal by NuSan (https://www.shadertoy.com/view/3sBGzV)
vec2 off=vec2(.001,0);
vec3 normal = normalize(map(pos)-vec3(map(pos-off.xyy), map(pos-off.yxy), map(pos-off.yyx)));
// Inigo Quilez color palette (https://iquilezles.org/www/articles/palettes/palettes.htm)
vec3 tint = .5+.5*cos(vec3(3,2,1)+material*.5+length(pos)*.5);
// lighting
float ld = dot(reflect(ray, normal), vec3(0,1,0))*0.5+0.5;
vec3 light = vec3(1.000,0.502,0.502) * sqrt(ld);
ld = dot(reflect(ray, normal), vec3(0,0,-1))*0.5+0.5;
light += vec3(0.400,0.714,0.145) * sqrt(ld)*.5;
// pixel color
fragColor.rgb = (tint + light) * shade;
// temporal buffer
fragColor = max(fragColor, texture(iChannel0, fragCoord/iResolution.xy) - 0.01);
}
// ----------------------------
// SHADERTOY CODE ENDS HERE -
// ----------------------------
Gif too big for upload so see original shadertoy sketch. Or @leondenise twitter.