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

uniform sampler2D  tex1;
#define  iChannel1 tex1
#define  sph       param
#define  bias      param[4].x
#define  detail    param[4].y
#define  blend     param[4].z

//   sph[0]=vec4(.5,.0,0.,1.);
//   sph[1]=vec4(-.5,.0,0.,.7);
//   sph[2]=vec4(-1.,.0,0.,.4);
//   sph[3]=vec4(.0,.5,0.,1.);
// const float bias=.06,detail=4.,blend = 1.0;

//--------------------------------------------------------------------------
bool RSI(in vec3 ro,in vec3 rd, in vec4 sphere,out vec2 t)
{
 vec3 oc = ro - sphere.xyz;
 float r = sphere.w+bias;
 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*detail - sin(p.zxy*detail)))*bias*sphere.w;
 float d=distance(sphere.xyz,q)-sphere.w;
 return d;
}

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

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

//--------------------------------------------------------------------------
vec3 boxmap( sampler2D sam, in vec3 p, in vec3 n)
{
  p=p*0.5+0.5;
  vec3 m = pow( abs(n), vec3(blend) );
  vec3 x = texture2D( sam, p.yz ).rgb;
  vec3 y = texture2D( sam, p.zx ).rgb;
  vec3 z = texture2D( sam, p.xy ).rgb;
  return (x*m.x + y*m.y + z*m.z)/(m.x+m.y+m.z);
}


//--------------------------------------------------------------------------
vec3 doBumpMap(in vec3 p, in vec3 n)
{
    const float bf=.1;
    const vec2 e = vec2(0.001, 0);
    mat3 m = mat3( boxmap(iChannel1, p - e.xyy, n), boxmap(iChannel1, p - e.yxy, n), boxmap(iChannel1, p - e.yyx, n));
    vec3 g = vec3(0.299, 0.587, 0.114)*m;
    g = (g - dot(boxmap(iChannel1,  p , n), vec3(0.299, 0.587, 0.114)) )/e.x;
    g -= n*dot(n, g);
    return normalize( n + g*bf );
}

//--------------------------------------------------------------------------
vec3 castray(vec3 ro, vec3 rd)
{
  float t = 1.0e16;
  vec3 sn=vec3(0.0);
  trace(ro,rd,sph[0],t,sn);
  trace(ro,rd,sph[1],t,sn);
  trace(ro,rd,sph[2],t,sn);
  trace(ro,rd,sph[3],t,sn);
  if (t>1.0e5) discard;
  vec3 sp = ro + t * rd;
  sn = doBumpMap(sp, sn);

  const float ambience = 0.125;
  float diff = max( dot(sn, lhtdir), 0.0);
  float spec = pow(max( dot( reflect(-lhtdir, sn), -rd ), 0.0 ), 32.);
  vec3  col  = boxmap(iChannel1, sp, sn);
  return col*(diff + spec + 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. );
}





