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

#define  mode        param[0].x
#define  density1    param[1].x
#define  density2    param[1].y
#define  thickness1  param[1].z
#define  thickness2  param[1].w

#define  iGlobalTime sparam.x
uniform  sampler2D tex0;
uniform  sampler2D tex1;
uniform  sampler2D tex2;
#define  iChannel0 tex0
#define  iChannel1 tex1
#define  iChannel2 tex2

const float xy_scale=4.,noise_scale=16./xy_scale;



bool RSI(in vec3 ro,in vec3 rd,in vec3 scale,out vec2 res)
{
  ro*=scale;
  rd*=scale;
  float k=1.0/length(rd);
  rd *=k;
  float b = dot(ro,rd);
  float c = dot(ro,ro) - 1.0;
  float d = b*b - c;

  if(d <= 0.0) return false;
  d = sqrt(d);
  res=vec2(-b - d,-b + d)*k;
  return true;
}

// #define _FAST

#ifdef _FAST

uniform  sampler3D tex4;
/*
_________________________________________________________*/
float perlin(vec3 q) // [-.4;.6]
{
 return texture(tex4,q*.125).r-.4;
}

#else

float noise(vec3 x) // [0;1]
{
  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 = texture( tex0, (uv+ 0.5)/256.0).yx;
  return mix( rg.x, rg.y, f.z );
}

float perlin(vec3 q) // [-.4;.6]
{
 //  q -= vec3(0.0,0.1,1.0)*iGlobalTime;
 float f=-0.4,k=.53;
 for (int i=0;i<4;i++)
 {
  f += k*noise( q );
  q *= 2.02;
  k*=.5;
 }
 return f;
}

#endif



float weight(vec3 p,vec4 tc,float thickness)
{
 p.z/=max(1.0e-16,thickness*(tc.a));
 p=abs(p)/xy_scale;
 float t=1.-sqrt(length(p));
 return t;
}

vec4 raymarch( in vec3 ro, in vec3 rd)
{
  const float k=0.7071/(xy_scale*(1.+.375));
  vec4 sum = vec4(0.0);
  vec2 tt;
  if (!RSI(ro,rd,vec3(k,k,k/thickness1),tt)) discard;
  if (mode>0.0)
  {
   vec3 pos =  ro-rd*(ro.z/rd.z);
   vec4 col  = mode==1.0 ? texture(iChannel1,pos.xy*vec2(.5,-.5)/xy_scale+.5) : texture(iChannel2,pos.xy*vec2(.5,-.5)/xy_scale+.5);
   return col.aaaa;
  }
  tt.x=max(0.,tt.x);
  float t = tt.x,t0=sqrt(tt.x)-tt.x;
  t+=0.15*texture  (iChannel0,rd.xy*vec2(280.,270.)).r; // dithering
  for(int i=0; i<200; i++)
  {
   if( sum.a > 0.99 ) break;
   vec3  pos = ro + t*rd;
   vec3  txc  = pos*vec3(.5,-.5,.5)/xy_scale+.5;
   vec4  tc1  = texture(iChannel1,txc.xy);
   vec4  tc2  = texture(iChannel2,txc.xy);
   float w1   = weight(pos,tc1,thickness1);
   float w2   = weight(pos,tc2,thickness2);
   if (w1>0.||w2>0.)
   {
    float wc   = abs(perlin(pos*noise_scale));
    w1=max(1.0e-26,w1*wc);
    w2=max(1.0e-26,w2*wc);
    float ww    = w1+w2;
    if( ww>1.e-25 )
    {
     tc1.rgb  = sqrt(tc1.rgb*texture(iChannel1,mix(txc.xy,txc.xz,txc.z)).rgb);
     tc2.rgb  = sqrt(tc2.rgb*texture(iChannel2,mix(txc.xy,txc.zy,txc.z)).rgb);
     ww=clamp(ww,0.,1.);
     float k  = 1./(w1+w2);
     vec4 col = (tc1*w1+tc2*w2)*k;
     col.a   *= ww*k*(w1*density1+w2*density2);
     col.rgb *= col.a;
     sum     += col*(1.0-sum.a);
     t       += max(0.03,(0.01+0.02*(t+t0))*(1.0-ww)); if (t>=tt.y) break;
     continue;
    }
   }
   t += max(0.03,(0.01+0.02*(t+t0))*2.);
   if (t>=tt.y) break;
  }
  return clamp( sum, 0.0, 1.0 );
}

void main()
{
  vec3 ro   =  camera.xyz*camera.w*xy_scale;
  vec3 rd   =  normalize(dir.xyz-camera.xyz);
  vec4 col  =  raymarch( ro, rd );
  fragColor =  col;
}
