//https://www.shadertoy.com/view/3sKGWw

/*
    Copyright (c) 2019 al-ro

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
*/

//Based on https://www.youtube.com/watch?v=3CycKKJiwis

// https://www.shadertoy.com/view/4djSRW
float random(vec2 p){
   vec3 p3  = fract(vec3(p.xyx) * 0.1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

vec2 random2(vec2 p){
	vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.xx + p3.yz) * p3.zy);
}

//https://www.shadertoy.com/view/3s3GDn
float getGlow(float dist, float radius, float intensity){
    return pow(radius/dist, intensity);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ){
    
    float t = 1.0 + iTime * 0.06;
    float layers = 10.0;
    float scale = 32.0;
    float depth;
    float phase;
    float rotationAngle = iTime * -0.1;
    float size;
    float glow;
    //Iteration step size for outermost loop
    float del = 1.0/layers;
    
   	vec2 uv;
    //Value of floor(uv)
    vec2 fl;
    vec2 local_uv;
    vec2 index;
    vec2 pos;
    //Seed for random values
    vec2 seed;
    vec2 centre;    
    //The indices of 3x3 cells surrounding the fragment
    vec2 cell;
    //To move the focus of the camera in a circle
    vec2 rot = vec2(cos(t), sin(t));
    
    //To rotate layers
    mat2 rotation = mat2(cos(rotationAngle), -sin(rotationAngle), 
                         sin(rotationAngle),  cos(rotationAngle));
 	vec3 col = vec3(0);
    vec3 tone;
    
    //For all layers
    for(float i = 0.0; i <= 1.0; i+=del){
        //Find depth from layer index and move it in time
        depth = fract(i + t);
        
        //Move centre in a circle depending on the depth of the layer
        centre = rot * 0.2 * depth + 0.5;
        
        //Get uv from the fragment coordinates, rotation and depth
    	uv = centre-fragCoord/iResolution.x;
        uv *= rotation;
    	uv *= mix(scale, 0.0, depth);
        fl = floor(uv);
        //The local cell coordinates. uv-fl == frac(uv)
        local_uv = uv - fl - 0.5;

        
        //For a 3x3 group of cells around the fragment, find the distance from the points 
        //of each to the current fragment and draw an accumulative glow accordingly
        //The local cell is (0,0)
       	for(float j = -1.0; j <= 1.0; j++){
            for(float k = -1.0; k <= 1.0; k++){
        		cell = vec2(j,k);
            
                //Cell index
        		index = fl + cell;
        
                //Cell seed
        		seed = 128.0 * i + index;
                
        		//Get a random position in the considered cell
        		pos = cell + 0.9 * (random2(seed) - 0.5);
        
        		//Get a random phase
        		phase = 128.0 * random(seed);
                //Get colour from cell information
        		tone = vec3(random(seed), random(seed + 1.0), random(seed + 2.0));
        
        		//Get distance to the generated point, fade distant points
                //and make glow radius pulse in time
        		size = (0.1 + 0.5 + 0.5 * sin(phase * t)) * depth;
        		glow = size * getGlow(length(local_uv-pos), 0.07, 2.5);
                //Add white core and glow
        		col += 5.0 * vec3(0.02 * glow) + tone * glow;
        	}
        }
    }
    
    //Tone mapping
    col = 1.0 - exp(-col);
    
    //Gamma
    col = pow(col, vec3(0.4545));
    
    //Output to screen
    fragColor = vec4(col,1.0);
}