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

bae #016 ~ Slab Steps

Rain drops on screen – notexture

starfield angled

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments