Josip Å tajdohar

2D global ilumination:


                uniform sampler2D tDiffuse;
                uniform float iTime;
                uniform vec2 iResolution;
                varying vec2 sv;

                #define pi acos(-1.)
                #define aTime 0.2 * iTime 
                #define SAMPLES 32
                #define TRACE_LIM 20
                #define REFLECTION_DEAPTH 4

                #define MAX_DIST 1.5e1
                #define MIN_DIST 1e-3
                #define BIAS 1e-2
                #define EPSILON 1e-3
                #define LATTICE_MAX 8.0
                #define F1 4.6
                #define F2 F1 / (LATTICE_MAX * 2.0 * pi)

                float atanT1(float x, float y, float a, float b, float f)
                {
                    float xma = x - a;
                    float ymb = y - b;
                    float xx = x * x;
                    float yy = y * y;
                    float xxyy = xx + yy;
                    return f + (ymb * xx - xma)/xxyy;
                }

                float atan2T1(float x, float y, float a, float b, float f)
                {
                    float signedPi = sign(y) * pi;
                    float piAmount = float(0.0 < x);
                    float r = atanT1(x, y, a, b, f) + piAmount * signedPi;


                    if(x == 0.0)
                    {
                        r = signedPi * 0.5;
                    }

                    return r;
                }

                float approxAtan(float z)
                {
                    //https://www.dsprelated.com/showarticle/1052.php
                    //return (0.972394 - 0.191948 * z * z) * z;

                    //http://www-labs.iro.umontreal.ca/~mignotte/IFT2425/Documents/EfficientApproximationArctgFunction.pdf (7)
                    return 0.25 * pi + 0.273 * z * (1.0 - abs(z));
                }

                float approxAtan2(float y, float x)
                {
                    float z = y / x;
                    float signedPi = sign(y) * pi;
                    float piAmount = float(0.0 < x);
                    float r = approxAtan(z) + piAmount * signedPi;


                    if(x == 0.0)
                    {
                        r = signedPi * 0.5;
                    }

                    return r;
                }

                //Based on https://www.shadertoy.com/view/lldcDf, https://github.com/miloyip/light2d, https://dev.to/khalilsaboor/fibonacci-recursion-vs-iteration--474l
                float random (in vec2 st) 
                {
                    return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                }

                mat2 r2(float a) 
                {
                    float c = cos(a), s = sin(a);
                    return mat2(c,s,-s,c);
                }

                float sphereSDF(vec2 p, float size, out vec2 polCoords) 
                {
                    float r = length(p);
                    //float f = sin(p.y);// + cos(p.x);
                    float f = atan(p.y, p.x);
                    //float f = atan2T1(p.x, p.y, 0.0, 0.0, 0.0);
                    //float f = approxAtan(p.y/ p.x);
                    polCoords = vec2(r, f);
                    return r - size;
                }

                float distortedWalls(vec2 p, vec2 size)
                {
                    vec2 polCoords = vec2(0.0);
                    
                    //      if 6.0 * sin(iTime) would go in steps from 0 to 6 this would be a prittier pattern
                    //      but the overuse of atan is making the performance drop drasticly...
                    float d = sphereSDF(p, size.y, polCoords);
                    d += 0.2 * sin(floor(LATTICE_MAX * fract(F2 * iTime)) * polCoords.y);

                    //float d = length(p) - size.y;
                    //float sinTime = sin(0.25 * iTime);
                    //d += (0.2 * sin(p.x) + 0.8 * cos(p.y)) * pow(sinTime, 8.0); 
                    return -d;
                }

                //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                //! Reflect Global Ilumination
                struct PBRstruct
                {
                    float dist;
                    vec3 color;
                    float emissive;
                    float reflectivity;
                    float eta;
                };

                PBRstruct unionOp(PBRstruct a, PBRstruct b)
                {
                    if(a.dist < b.dist) return a;
                    return b;
                }

                PBRstruct intersectOp(PBRstruct a, PBRstruct b)
                {
                    if(a.dist > b.dist) return a;
                    return b;
                }

                PBRstruct subtractOp(PBRstruct a, PBRstruct b)
                {
                    PBRstruct c = a;
                    if(a.dist > -b.dist) c.dist = a.dist;
                    else c.dist = -b.dist;
                    return c;
                }

                PBRstruct reflectScene(vec2 pos)
                {
                    float ball1Size = 0.8;
                    vec2 ball1Coords = vec2(0.0);
                    float ball1Dist =  sphereSDF(pos - vec2(2.0 * sin(F1 * iTime), 0.0), ball1Size, ball1Coords);
                    //https://iquilezles.untergrund.net/www/articles/palettes/palettes.htm
                    vec3 ball1Col = vec3(0.4, 0.5, 0.6) + vec3(0.2, 0.25, 0.3) * cos(2.0 * pi * (vec3(0.5, 2.0, 3.5) * iTime + abs(ball1Coords.y) * vec3(.2, 0.4, 0.7)));
                    ball1Col *= ball1Size/ball1Coords.x;
                    PBRstruct ball1 = PBRstruct(ball1Dist, ball1Col, 1.7, 0.0, 1.0);

                    vec2 ball2Size = vec2(8.0, 5.0);
                    float ball2Dist = distortedWalls(pos - vec2(0,0), ball2Size);
                    vec3 ball2Col = vec3(0.1, 0.1, 0.2);
                    PBRstruct ball2 = PBRstruct(ball2Dist, ball2Col, 0.10, 0.8, 1.0);

                    return unionOp(ball2, ball1);
                }

                void gradient(vec2 pos, inout vec2 norm)
                {
                    vec2 deltaX = vec2(EPSILON, 0.0);
                    vec2 deltaY = vec2(0.0, EPSILON);
                    norm.x = (reflectScene(pos + deltaX).dist - reflectScene(pos - deltaX).dist)/ 2.0 * EPSILON;
                    norm.y = (reflectScene(pos + deltaY).dist - reflectScene(pos - deltaY).dist)/ 2.0 * EPSILON;
                    norm = normalize(norm);
                }

                void reflect(vec2 dir, vec2 norm, inout vec2 ref)
                {
                    float DdotN = dot(dir, norm) * 2.0;
                    ref = dir - DdotN * norm; 
                    ref = normalize(ref);
                }

                void traceRef(inout vec2 pos, inout vec2 dir, inout vec3 c, inout float reflectivity)
                {
                    float d = 0.0;
                    for (int i = 0; i < TRACE_LIM; i++) 
                    {
                        PBRstruct pbrObject = reflectScene(pos);
                        if (pbrObject.dist < MIN_DIST)
                        {
                            c += pbrObject.color * pbrObject.emissive * reflectivity;
                            reflectivity *= pbrObject.reflectivity;
                            vec2 normal = vec2(0.0);
                            vec2 refl = vec2(0.0);

                            float signOfDist = sign(pbrObject.dist);


                            if(pbrObject.reflectivity > 0.0)
                            {
                                gradient(pos, normal);
                                normal *= signOfDist;
                                reflect(dir, normal, refl);
                            }

                            pos += BIAS * normal;
                            dir = refl;

                            return;
                        } 
                        if (d > MAX_DIST) break;
                        d += pbrObject.dist;
                        pos += dir * pbrObject.dist;
                    }
                }

                void multiTrace(vec2 pos, vec2 dir, out vec3 col)
                {
                    float reflectivity = 1.0;
                    for (int i = 0; i < REFLECTION_DEAPTH; i++)
                    {
                        traceRef(pos, dir, col, reflectivity);
                        if(reflectivity <= 0.0 ) break;
                    }
                }

                //! Reflect Global Ilumination
                //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                vec3 sample(vec2 uv)
                {
                    vec3 col = vec3(0,0,0);
                    for (int i = 0; i < SAMPLES; i++) {
                        float t = (float(i) + random(uv+float(i)+iTime)) / float(SAMPLES) * 2. * pi;
                        vec3 c = vec3(0.0);
                        multiTrace(uv, vec2(cos(t), sin(t)), c);
                        col += c;
                    }
                    col /= float(SAMPLES);
                    return col;
                }

                void main()
                {
                    // Normalized pixel coordinates (from 0 to 1)
                    vec2 uv = (sv - 0.5) * iResolution.xy / iResolution.y;
                    uv *= 10.;
                    vec3 col = sample(uv);
                    gl_FragColor = vec4(col, 1.0);
                }