Attack of the Smarties

Shader code
shader_type canvas_item;

// Variation on Ford's Circles stuff: http://nrich.maths.org/6594
// Inspired by Sarconix: https://www.shadertoy.com/view/4dsXWs

////////////////////////////// defines
// Diameter of biggest circle
#define DIAMETER 100.0
// Nb of iterations = nb of circles of decreasing size to build
#define ITERATIONS 50
// Time in second(s) to zoom in or out
#define ZOOM_TIME 10.0
// Heightmap coef [0.0 100.0]
#define HEIGHT_COEF 4.0
// Light direction
#define LIGHT_DIR vec3(1.0, 1.0, 1.0) 
// Ambiant lighting
#define LIGHT_AMBIANT vec3(0.1, 0.1, 0.1)
// Mix between original texture/procedural circles [0.0 1.0]
#define MIX_FACTOR 0.5
// Point of view
#define EYE vec3(0.57735, 0.57735, 0.57735)
// Strength of specular reflection [0.0 1.0]
#define SPEC_FACTOR 0.45

#define pi 3.1415926535897932384626433832795
#define hfpi 1.5707963267948966192313216916398
#define PI pi
#define HFPI hfpi

uniform sampler2D iChannel0;
uniform vec4 iMouse;

////////////////////////////// methods
// Most important method: Ford

float AnimateDiameter()
{
    float i = floor(TIME / ZOOM_TIME);
    float r = (TIME - ZOOM_TIME * i) / ZOOM_TIME;
    float sinr = pow(sin(HFPI * r), 2.0);
    float k = ( mod(i, 2.0) == 0.0 ? sinr : 1.0 - sinr );
 	return max(k*DIAMETER, 1.0);
}

// Compute normal at position pos, as if we were on an half-sphere with center (0,0)
vec3 ComputeNormal(vec2 pos, float radius)
{
    // Determine the distance to the center of the circle.
	float d = length(pos);
	float h = sqrt(radius*radius - d*d);
	return (normalize(vec3(pos.x,pos.y,HEIGHT_COEF*h)));
}

vec3 Ford(vec2 posSample, vec2 iRes)
{   
    float diameter = AnimateDiameter();
    float radius = diameter / 2.0;
    
    vec2 screenPos = posSample*iRes.xy - (iRes.xy / 2.0);
    
    //position of sample relative to circle: (0,0) = center
	vec2 pos = vec2(0.0);
    
    bool inside = false; //true iff sample is in a circle
	float rSmall = 0.0;
    
    // Build biggest circles of radius: r0
	float r0 = radius;
	pos = mod(screenPos, vec2(diameter, diameter)) - vec2(r0, r0);
	if (length(pos) < r0)
	{
		inside = true;
		rSmall = r0;
	}
    
    // Build smaller circles surrounded by 4 big circles
    float r1 = 0.0;
	vec2 mod1 = vec2(0.0);
	if (!inside)
	{
        mod1 = mod(screenPos + vec2(r0, r0), vec2(diameter, diameter)) - vec2(r0, r0);
		r1 = (sqrt(2.0) - 1.0)*r0;
		pos = mod1;
		if (length(pos) < r1)
		{
			inside = true;
			rSmall = r1;
		}
	}
    
    // Iterations - begin
	// We build circles smaller at each iteration
	// radius r[n] of nth circle is given by:
	//
	//            (r[0] - r[1] - 2*r[2] - ... - 2*r[n-1])^2      
	// r[n] = ----------------------------------------------------
	//         2 * (r[0] + r[0] - r[1] - 2*r[2] - ... - 2*r[n-1])
	//
	// 
	
	if (!inside)
	{
		float rBig = 0.0;
		float distLeft = r0 - r1; //distance to 2 big circles point-of-contact
		float r = r1; //radius of current circle
		
		for (int n=0; n<ITERATIONS; n++)
		{		
			if (!inside)
			{
				rSmall = (distLeft - 2.0*rBig)*(distLeft - 2.0*rBig)/(2.0*(r0 + distLeft - 2.0*rBig));	
			
				//West
				pos = mod1 + vec2(rBig + rSmall + r, 0.0);
				if (length(pos) < rSmall)
				{
					inside = true;
				}
				if (!inside)
				{
					//East
					pos = mod1 - vec2(rBig + rSmall + r, 0.0);
					if (length(pos) < rSmall)
					{
						inside = true;
					}	
					if (!inside)
					{
						//North
						pos = mod1 - vec2(0.0, rBig + rSmall + r);
						if (length(pos) < rSmall)
						{
							inside = true;
						}
						if (!inside)
						{
							//South
							pos = mod1 + vec2(0.0, rBig + rSmall + r);
							if (length(pos) < rSmall)
							{
								inside = true;
							}
						}
					}
				}
				
				//updates for next iteration
				r = r + rBig + rSmall;
				distLeft = distLeft - 2.0*rBig;
				rBig = rSmall;				
			}
		}
	}
	// Iterations - end
    
	// Now we're done with building circles, we give them some 3d look & feel	
        
    ///////////////////////// post-production
    
    //heightmap
    vec3 normal = vec3(0.57735);
    vec2 centerC = vec2(0.0);
    float distance2center = radius;
    if (inside)
	{
		centerC = posSample - pos/iRes.xy;
		distance2center = length(pos) + r0;
		normal = ComputeNormal(pos, rSmall);
	}
    
    //sample texture using coordinates of circle's center
    // NOTE: Use a large negative bias to effectively disable mipmapping, which would otherwise lead
    // to sampling artifacts where the UVs change abruptly at the pixelated block boundaries.
    centerC.y = 1.0 - centerC.y; //upside-down correction
	vec3 colTex = texture(iChannel0, centerC, -32.0).rgb;        
	
	vec3 light = normalize(LIGHT_DIR);
	
	// Point light
    vec3 colProc = vec3(0.8, 0.8, 0.8) * clamp(dot(normal, light), 0.0, 1.0);

	// Ambiant ligth
    colProc += LIGHT_AMBIANT;
			
	vec3 colFinal = mix(colTex, colProc, MIX_FACTOR);	
	
	// Reflection
	vec3 eye = normalize(EYE);
    vec3 ref = reflect(eye, normal);
        
    // Specular
    float spec = pow(clamp(dot(light, ref), 0.0, 1.0), 16.0);
    colFinal += SPEC_FACTOR * spec;	
	
		
    // Set the final fragment color.
	return colFinal;
} 

// Sample a procedural texture (anti-aliasing)
// Stolen from IQ: https://www.shadertoy.com/view/MdjGR1
vec3 FordAA( vec2 uv, vec2 iRes )
{
	#define SAMPLING_STRENGTH 1000000000.0
	#define NB_SAMPLES 3 //0: no anti-aliasing
	
	if (NB_SAMPLES == 0)
	{
		return Ford( uv, iRes );
	}
	else
	{
		// calc texture sampling footprint		
		vec2 ddx = dFdx( uv ); 
		vec2 ddy = dFdy( uv ); 
	
		int sx = 1 + int( clamp( SAMPLING_STRENGTH*length(ddx), 0.0, float(NB_SAMPLES-1) ) );
		int sy = 1 + int( clamp( SAMPLING_STRENGTH*length(ddy), 0.0, float(NB_SAMPLES-1) ) );

		vec3 no = vec3(0.0);

		for( int j=0; j<NB_SAMPLES; j++ )
		for( int i=0; i<NB_SAMPLES; i++ )
		{
			if( j<sy && i<sx )
			{
				vec2 st = vec2( float(i), float(j) ) / vec2( float(sx),float(sy) );
				no += Ford( uv + st.x*ddx + st.y*ddy, iRes);
			}
		}

		return no / float(sx*sy);
	}
}

vec2 Rot2d(vec2 uv, float angle){
	mat2 rot_matrix = mat2(vec2(sin(angle), -cos(angle)), vec2(cos(angle), sin(angle)));
	return uv * rot_matrix;
}

////////////////////////////// main
void fragment()
{
	vec2 iResolution = 1.0 / SCREEN_PIXEL_SIZE;
	vec2 uv = UV;   
   
    //pan
  	uv -= iMouse.xy / iResolution.xy;
		
	uv.y = 1.0 - uv.y;
	
	vec3 col = FordAA( uv , iResolution);
    // Set the final fragment color.
	COLOR = vec4(col,1.0);
}
This shader is a port from an existing Shadertoy project. Shadertoy shaders are by default protected under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) license unless anything else has been stated by the author. For more info, see our License terms.

More from RayL019

vortex and shrink

Cosine Water

Fork Nixie Tube Clock

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments