uniform sampler2D tDiffuse;
uniform float iTime;
uniform vec2 iResolution;
varying vec2 sv;
#define pi acos(-1.)
#define aTime 0.2 * iTime
#define SAMPLES 128
#define TRACE_LIM 32
#define REFLECTION_DEAPTH 4
#define MAX_DIST 1.5e1
#define MIN_DIST 1e-3
#define BIAS 1e-2
#define EPSILON 1e-3
//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) {
return length(p) - size;
}
float boxSDF(vec2 p, vec2 size) {
vec2 r = abs(p) - size;
return min(max(r.x, r.y),0.) + length(max(r,vec2(0,0)));
}
float lenseSDF(vec2 p, vec2 size)
{
float box = boxSDF(p, size);
float sphere = sphereSDF(p - vec2(0.0, 1.0) * size.x, size.x);
return max(-sphere, box);
}
float lenseSDF2(vec2 p, vec2 sizeAndFocus)
{
float sphere1 = sphereSDF(p - vec2(sizeAndFocus.y, 0.0), sizeAndFocus.x);
float sphere2 = sphereSDF(p + vec2(0.0, sizeAndFocus.y), sizeAndFocus.x);
return max(sphere1, sphere2);
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//! 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)
{
PBRstruct ball1 = PBRstruct(sphereSDF(pos - vec2(1,3), 1.), vec3(1,.1,.1), 0.8, 0.0, 1.0);
PBRstruct ball2 = PBRstruct(sphereSDF(pos - vec2(-1,1), 0.5), vec3(0.02, 0.82,.02), 0.65, 0.0, 1.0);
PBRstruct lens = PBRstruct(lenseSDF(pos - vec2(0,-1), 5.0 * vec2(0.5, 0.25)), vec3(.2,.3, 0.3), 0.1, 0.5, 1.3);
PBRstruct lens2 = PBRstruct(lenseSDF2(pos - vec2(-5.5, 3.5), vec2(2.0, 1.9)) ,vec3(.2,.3, 0.3), 0.1, 0.20, 1.6);
//return unionOp(ball2, lens);
return unionOp(unionOp(unionOp(ball1, ball2), lens2), lens);
}
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);
}
int refract(vec2 dir, vec2 norm, float eta, inout vec2 ref)
{
float DdotN = dot(dir, norm);
float k = 1.0 - eta * eta * (1.0 - DdotN * DdotN);
if(k < 0.0) return 0; //Total internal reflection
float a = eta * DdotN + sqrt(k);
ref = eta * dir - a * norm;
ref = normalize(ref);
return 1;
}
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 (abs(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 traceRefRef(inout vec2 pos, inout vec2 dir, inout vec3 c, inout float reflectivity, inout float eta)
{
float d = 0.0;
for (int i = 0; i < TRACE_LIM; i++)
{
PBRstruct pbrObject = reflectScene(pos);
if (abs(pbrObject.dist) < MIN_DIST)
{
c += pbrObject.color * pbrObject.emissive * reflectivity;
reflectivity *= pbrObject.reflectivity;
eta = pbrObject.eta;
vec2 normal = vec2(0.0);
vec2 refl = vec2(0.0);
gradient(pos, normal);
float signOfDist = sign(pbrObject.dist);
normal *= signOfDist;
eta = pow(pbrObject.eta, -signOfDist);
int isExternal = refract(dir, normal, eta, refl);
vec2 refractionDir = refl;
if(isExternal == 0)
{
reflectivity = 1.0;
eta = 0.0;
}
if(pbrObject.reflectivity > 0.0)
{
reflect(dir, normal, refl);
}
float isReflection = 1.0;
if(eta > 0.0)
{
isReflection = sign(random(iTime + pos + dir) - 0.5);
}
//isReflection = -1.0;
pos = pos + isReflection * BIAS * normal;
dir = mix(refractionDir, refl, isReflection);
reflectivity = mix(1.0 - reflectivity, reflectivity, isReflection);
return;
}
if (d > MAX_DIST) break;
d += pbrObject.dist;
pos += dir * pbrObject.dist;
}
}
void multiTraceRef(vec2 pos, vec2 dir, out vec3 col)
{
float reflectivity = 1.0;
float eta = 1.0;
for (int i = 0; i < REFLECTION_DEAPTH; i++)
{
reflectivity *= pow(2.0, float(i - 1));
traceRefRef(pos, dir, col, reflectivity, eta);
if(reflectivity <= 0.0 && eta <= 0.0) break;
}
}
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);
multiTraceRef(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);
}