#define ncircles 6

in  vec4 dir;
out vec4 fragColor;
uniform vec4 lhtdir;
uniform vec4 param[ncircles+1];
uniform vec4 camera; // xyz- camera origin  , w - 1/rad

uniform sampler2D  tex0;
uniform sampler2D  tex1;
#define  iChannel0 tex0
#define  iChannel1 tex1
#define  sph       param
#define  rough_amplitude    param[ncircles].x
#define  rough_scale        param[ncircles].y
#define  texture_blend      param[ncircles].z
#define  bump               param[ncircles].w

float _noise3( in vec3 x )
{
  vec3 p = floor(x);
  vec3 f = fract(x);
  f = f*f*(3.0-2.0*f);
  vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;
  vec2 rg = texture2D( iChannel0, (uv+0.5)/256.0).yx;
  float v=mix( rg.x, rg.y, f.z );
  return v;
}

float fbm( vec3 p,float cloudness)
{
  const  int noctaves=5;
  const  float lacunarity = 3.;
  const  float gain = .5;
  float icloudness=1.0-cloudness;
  float  r=0.0,f = 4.0*icloudness;
  vec3 pn=p;
  for (int i = 0; i < noctaves; i++ )
  {
   p   *= lacunarity;
   r    = r+f * (_noise3(p) - icloudness);
   f    *= gain;
  }
  return clamp(r,0.,1.);
}

//--------------------------------------------------------------------------
bool RSI(in vec3 ro,in vec3 rd, in vec4 sphere,out vec2 t)
{
 vec3 oc = ro - sphere.xyz;
 float r = sphere.w+rough_amplitude;
 float b = dot( oc, rd );
 float c = dot( oc, oc ) - r*r;
 float d = b*b - c;
 if (d<=0.0) { t.x=t.y=1.e6; return false; }
 d = sqrt(d);
 t.x=(-b - d);
 t.y=(-b + d);
 return true;
}

//--------------------------------------------------------------------------
float map(vec3 p,vec4 sphere)
{
 vec3 q = p + (cos(p*rough_scale - sin(p.zxy*rough_scale)))*rough_amplitude*sphere.w;
 float d=distance(sphere.xyz,q)-sphere.w;
 return d;
}

//--------------------------------------------------------------------------
void trace(in vec3 ro,in vec3 rd,in vec4 sphere,out vec4 sn)
{
 vec2 ts;
 if (!RSI(ro,rd,sphere,ts)) { sn.w=1.0e16; sn.xyz=vec3(0.0); return; }
 float tt=max(ts.x,0.0);
 vec3  sp;
 for(int i = 0; i < 128; i++)
 {
  if (tt>=ts.y) { sn.w=1.0e16; sn.xyz=vec3(0.0); return; }
  sp=ro+rd*tt;
  float d = map(sp,sphere);
  if(abs(d)<0.0025*(ts.x*.125 + 1.)) break;
  tt += d*.8;
 }
 sn.xyz=normalize(sp-sphere.xyz);
 sn.w  =tt;
}


//--------------------------------------------------------------------------
mat2 rot(float a)
{
  float sa = sin(a);
  float ca = cos(a);
  return mat2(ca,sa,-sa,ca);
}

//--------------------------------------------------------------------------
vec3 texmap3d( sampler2D sam, in vec3 p, in vec3 n)
{
  p=p*0.5+0.5;
  vec3 m = pow( abs(n), vec3(texture_blend) );
  vec3 x = texture2D( sam, p.yz ).rgb;
  vec3 y = texture2D( sam, p.zx ).rgb;
  vec3 z = texture2D( sam, p.xy ).rgb;
  return (2.*fbm(p*6.,.9))*(x*m.x + y*m.y + z*m.z)/(m.x+m.y+m.z);
  // return vec3(2.*fbm(p*6.,.9));
}


//--------------------------------------------------------------------------
vec3 doBumpMap(in vec3 p, in vec3 n,in vec3 c0,in float dist)
{
    vec2 e = vec2(1.0/textureSize(iChannel1,0).x, 0);
    const vec3 W = vec3(0.299, 0.587, 0.114);
    mat3 m = mat3( texmap3d(iChannel1, p - e.xyy, n), texmap3d(iChannel1, p - e.yxy, n), texmap3d(iChannel1, p - e.yyx, n));
    vec3 g = W*m;
    g = (g - dot(c0, W) )/e.x;
    g -= n*dot(n, g);
    return normalize( n + g*bump/max(1.,dist*.25) );
}

//--------------------------------------------------------------------------
vec3 castray(vec3 ro, vec3 rd)
{
  float t = 1.0e16;
  vec4 slist[ncircles];
  for (int n=0;n<ncircles;n++)
  {
   if (sph[n].w<=0.0) break;
   trace(ro,rd,sph[n],slist[n]);
   t=min(t,slist[n].w);
  }
  if (t>1.0e5) discard;// return vec3(.0);
  vec3 sn=vec3(0.0);
  // float ww=0.0,tt=0.0;
  for (int n=0;n<ncircles;n++)
  {
   if (sph[n].w<=0.0) break;
   float w=abs(t-slist[n].w)*100.;
   w=1./(w*w+4.);
   sn+=w*slist[n].xyz;
   // tt+=w*t;
   // ww+=w;

  }
  // tt/=ww;
  sn=normalize(sn);

  vec3 sp = ro + t * rd;
  vec3  col  = texmap3d(iChannel1, sp, sn);
  sn = doBumpMap(sp, sn,col,length(ro));

  const float ambience = 0.125;
  float diff = max( dot(sn, lhtdir.xyz), 0.0);
  return col*(diff + ambience);
}

//--------------------------------------------------------------------------
void main()
{
  vec3 ro  =  camera.xyz*camera.w;
  vec3 rd  =  normalize(dir.xyz-camera.xyz);
  vec3 col  = castray(ro,rd);
  fragColor = vec4( col.rgb, 1. );
}





