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);
}