Files
bluflame/hgplus/ShaderMinifier/tests/real/lunaquatic.frag
2026-04-18 22:31:51 +02:00

392 lines
13 KiB
GLSL

// Lunaquatic
// This source is released exclusively for ShaderToy by rgba, for educational purposes only.
// Feel free to be inspired by this code and play around with it. If you make a production with the help
// of this code, it would be polite to greet our group in the intro or in the NFO file.
// Have fun! - xTr1m / BluFlame
#extension GL_EXT_gpu_shader4: enable
// .xy = pixel position
// .z = time
vec4 Y;
uniform vec2 resolution;
uniform float time;
// All data of our world
vec4 artifactPos;
vec3 lightPos, lightDir, ro, rd;
float FAR, EXPLOSIONTIME, pi, eps=0.0001;
float saturate(float x) { return clamp(x,0.0,1.0); }
float ftime(float t, float s, float e) { return (t-s)/(e-s); }
vec3 rotateY(vec3 v, float x)
{
return vec3(
cos(x)*v.x - sin(x)*v.z,
v.y,
sin(x)*v.x + cos(x)*v.z
);
}
vec3 rotateX(vec3 v, float x)
{
return vec3(
v.x,
v.y*cos(x) - v.z*sin(x),
v.y*sin(x) + v.z*cos(x)
);
}
// Pseudo random number base generator (credits go to iq/rgba)
float rnd(vec2 x)
{
int n = int(x.x * 40.0 + x.y * 6400.0);
n = (n << 13) ^ n;
return 1.0 - float( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0;
}
// Convert the cipher range from [-1,1] to [0,1]
float norm(float x)
{
return x * 0.5 + 0.5;
}
// Generate animated (t) caustic values
float caustic(float u, float v, float t)
{
return pow((
norm(sin(pi * 2.0 * (u + v + Y.z*t))) +
norm(sin(pi * (v - u - Y.z*t))) +
norm(sin(pi * (v + Y.z*t))) +
norm(sin(pi * 3.0 * (u - Y.z*t)))) * 0.3, 2.0);
}
// Generate cubic interpolated random values
float smoothrnd(vec2 x)
{
x = mod(x,1000.0);
vec2 a = fract(x);
x -= a;
vec2 u = a*a*(3.0-2.0*a);
return mix(
mix(rnd(x+vec2(0.0)),rnd(x+vec2(1.0,0.0)), u.x),
mix(rnd(x+vec2(0.0,1.0)),rnd(x+vec2(1.0)), u.x), u.y);
}
float height(vec2 x)
{
float maxV = Y.z - EXPLOSIONTIME;
float l = mix(1., max(0., artifactPos.w - artifactPos.y), 1.0 - ((maxV > 0.0 && length(x - artifactPos.xz) < maxV) ? 1.0 : 0.0)) /
pow(1./max(0., 1.0-length(artifactPos.xz-x)*0.8), 2.0);
x += length(x-ro.xy);
x *= min(length(x-ro.xy)*5.0, 4.0);
return caustic(x.x+Y.z*0.75, x.y*0.5, 0.3) * 0.006 +
caustic(x.x*0.1+Y.z*0.2, x.y*0.1, 0.02) * 0.125 -
0.15 - l*2.0;
}
// Calculates the water "waves". To reduce the bumpiness, increment the y-axis
vec3 getWaterNormal(vec3 p)
{
return normalize(vec3(
caustic(p.x * 160.0 - 12.0 * cos(10.0 * p.z), p.z * 140.0, 4.0),
8.0,
caustic(p.z * 160.0 - 12.0 * sin(10.0 * p.x), p.x * 140.0, 4.0)) * 2.0 - 1.0);
}
// Raymarch the terrain function, returns the distance from the ray origin to the terrain voxel
// This function was originally adopted from an implementation by iq/rgba
int traceTerrain(vec3 ro, vec3 rd, float maxt, out float depth)
{
float lh, ly, delt=0.0;
// advance our sample position from our nearplane to our farplane
for (float t = 0.1; t < maxt; t += delt)
{
// advance our ray
ro += rd * delt;
// get the height at the given sample 2d (!) position (we could enhance this by sampling a voxel and returning only the distance to the voxel)
depth = height(ro.xz);
if (ro.y <= depth)
{
// we need to know our improved (more accuracy here) real terrainposition and the old sampleposition
// also we precalculate the traveled ray distance (its not a ray anymore if we use stuff like refraction, eg but hey lets stick to this word)
depth = t - delt + delt*(lh-ly)/(ro.y-depth+lh-ly);
return 1;
}
// store our last height and last sampleposition on the y-axis
// we need this to calculate the improved terrainposition which will give us a smoother transition between our samplesteps (rd*delt)
lh = depth;
ly = ro.y;
// advance our steplength the more we travel the bigger our stepsize should be
// with this we are able to sample finer details near to our camera
delt = 0.002 + (t/(40.0 * clamp(rd.y+1.0,0.0,1.0))); //detail level
}
// we hit nothing
return 0;
}
vec3 calculateSkySub(vec3 rd)
{
return norm(smoothrnd(abs(rd.xy*rd.z+rd.y*2.0))) * vec3(0.15) +
norm(smoothrnd(1.5*abs(rd.xy*rd.z+rd.y+10.0)))* vec3(0.15) +
norm(smoothrnd(2.5*abs(rd.xy*rd.z+rd.y+20.0)))* vec3(0.15);
}
vec4 calcPlanet(vec3 ro, vec3 rd)
{
vec4 color = vec4(0.0);
vec3 planetPos = vec3(70.0, 20.0, 100.0);
float dist = dot(rd, normalize(planetPos-ro))-0.95;
if (dist>0.0)
{
dist = length(planetPos-ro)-dist*800.0;
vec3 p = ro+rd*dist;
vec3 n = normalize(planetPos-p);
vec2 uv = 0.5 + 0.5 * vec2(atan(n.z, n.x), acos(n.y)) / pi * vec2(5.0, 50.0);
color.rgb = max(0., 0.2+dot(normalize(p-lightPos), n)) *
(caustic(uv.x*0.5+Y.z*0.1, uv.y*0.5,0.)+0.5)*.15 * vec3(1.0,0.0,1.0);
color.a = 1.0;
}
else dist = FAR*99.0;
// hit with plane
vec3 pN = vec3(-0.96,0.96,-0.2);
float t = dot(pN, planetPos-ro) / dot(pN, rd);
if (t > 0.0 && t < dist)
{
float d = length(planetPos - (ro+rd*t));
if (d > 52.0 && d < 80.0)
color.rgb = mix(color.rgb, vec3(0.8, 0.64, 0.4), t / 200.0 * norm(sin((d-50.0)/30.0 * smoothrnd(vec2(d, 3.0)))));
color.a = color.a < 1.0 ? 3.0 * length(color) : color.a;
}
return vec4(max(vec3(0.0), color.xyz*0.3) * clamp(dot(rd, vec3(0.0,1.0,0.0))*8.0, 0., 1.0), color.a);
}
vec3 calculateSky(vec3 ro, vec3 rd, int addPlanet)
{
// atmospheric scattering+sun
vec3 color = max(vec3(0.0), (max(vec3(0.0), pow(dot(lightDir, rd), 6.0)) * .7 - rd.y) * mix(vec3(1.0,0.5,0.0), vec3(1.0), min(1.0, lightDir.y*1.5)) + lightDir.y * 3.0);
float phi = atan(rd.x, rd.z);
float theta = acos(rd.y / length(rd));
float coeff = smoothstep(0.0, 0.5, norm(0.5 * smoothrnd(300.0 * vec2(phi, theta))) + norm(0.75 * smoothrnd(500.0 * vec2(phi, theta))) - 1.25) * saturate(1.0-lightDir.y*5.0);
if (addPlanet>0)
{
// a planet
vec4 p = calcPlanet(ro, rd);
color += coeff*saturate(1.0-p.a) + p.rgb;
}
// the clouds
rd.xy += ro.xy*eps;
color += (calculateSkySub(normalize(rd + vec3(sin(Y.z*0.1),0.0,cos(Y.z*0.1)) * 0.1)*3.0) +
calculateSkySub(normalize(rd + vec3(sin(Y.z*0.1),0.0,cos(Y.z*0.1)) * 0.2)*5.0)*0.1 +
calculateSkySub(normalize(rd + vec3(sin(Y.z*0.1),0.0,cos(Y.z*0.1)) * 0.4)*7.0)*0.1 -
calculateSkySub(normalize(rd + vec3(sin(Y.z*0.2),0.0,0) * 0.5))*1.5) * saturate(rd.y+0.5);
return color;
}
float isoSurface(vec3 p)
{
float b = Y.z>80.0&&Y.z<112.0?1.0:0.0;
p = rotateX(rotateY(rotateX(rotateY(p - artifactPos.xyz, 3.0*Y.z), 3.0*Y.z), b*sin(3.0*Y.z+3.0*p.y)), b*sin(3.0*Y.z+3.0*p.x));
p *= 4.0 + 10.0 * max(0., Y.z - EXPLOSIONTIME);
return -0.4 +
p.x*p.x*p.x*p.x*p.x*p.x*p.x*p.x +
p.y*p.y*p.y*p.y*p.y*p.y*p.y*p.y +
p.z*p.z*p.z*p.z*p.z*p.z*p.z*p.z;
}
float traceIso(vec3 ro, vec3 rd, float mint, float maxt, float s)
{
float lt, liso, exact, delt = (maxt-mint)/s;
for (float t = mint; t < maxt; t += delt)
{
vec3 p = ro + t * rd;
float iso = isoSurface(p);
if (iso <= 0.0)
{
for(int i = 0; i < 9; i++)
{
exact = (lt + t) / 2.0;
if (isoSurface(ro + exact * rd) < 0.0) t = exact;
else lt = exact;
}
return exact;
}
lt = t;
liso = iso;
}
return FAR;
}
void calcBurn(vec2 x, vec3 normal, inout vec3 color)
{
float gd = length(x - artifactPos.xz);
float maxV = Y.z - EXPLOSIONTIME;
if (maxV > 0.0 && gd < maxV)
{
float minV = maxV*0.9;
if (gd < maxV-(maxV-minV))
{
float strength = saturate((gd-minV) / ((maxV-(maxV-minV)*2.0)-minV));
color *= (1.0-strength*1.5);
color += (1.0-strength*0.8) *
pow(norm(normal.x) + norm(normal.y),
2.0*norm(smoothrnd(0.4*Y.z+x*20.0))*
norm(smoothrnd(10.0 +x*5.0 ))+
1.
) * vec3(1.5, 0.75, 0.5);
}
if(gd > maxV-(maxV-minV)*2.0)
color += cos( (gd-minV) / (maxV-minV) * pi*0.5 ) * vec3(1.5, 0.75, 0.5) ;
}
}
vec3 calcScene(vec3 ro, vec3 rd)
{
float upperPlane = (0.1-ro.y) / rd.y;
float finalDepth = 200.0;
vec3 color = calculateSky(ro, rd, 1);
if (rd.y < -0.01 && traceTerrain(ro+rd*upperPlane, rd, finalDepth, finalDepth)>0) // prevent endless stuff and other funny shit
{
finalDepth += upperPlane;
vec3 pos = ro+rd*finalDepth;
vec3 normal = normalize(normalize(
vec3(
height(pos.xz - vec2(eps, 0.0)) - height(pos.xz + vec2(eps, 0.0)),
eps*2.0,
height(pos.xz - vec2(0.0, eps)) - height(pos.xz + vec2(0.0, eps))
)
) +
(getWaterNormal(pos*0.2)*1.5+getWaterNormal(pos*0.1)) * max(0., 1.0-finalDepth/FAR*7.0)
);
color = max(0., dot(normal, lightDir) // diffuse
+ pow(max(0., dot(normal, normalize(lightDir-rd))), 2.0) // specular
) *
calculateSky(pos, reflect(rd, normal), 1);
// burn
calcBurn(pos.xz, normal, color);
// depth fog
color = mix( color,
calculateSky(ro, rd, 0),
saturate(finalDepth/FAR*1.6 + saturate(dot(normalize(normal+rd*.2), rd)))
); // sky refl and fog
}
return color;
}
void main()
{
FAR = 9.0;
EXPLOSIONTIME = 127.0;
pi = 3.1416;
Y.xy = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
Y.z = time;
rd = normalize(vec3(Y.xy - 0.5, 1.0));
artifactPos = vec4(10.0, norm(sin(Y.z*1.0+4.0)), 13.0, 0.0);
ro = vec3(.0, .25, 0.);
float t = Y.z;
if (t < 16.0) // 1 intro
{
t = ftime(t,0.0,16.0);
t = 1.0-pow(1.0-t, 2.);
lightPos = vec3(-400.0, 100.0 - t*450.0, 1000.0);
t=(t-1.0)*0.7;
rd = rotateX(rd, -t);
}
else if (t < 32.0) // 1 intro
{
t=ftime(t,16.0,32.0);
t = 1.0-pow(1.0-t, 2.);
lightPos = vec3(-400.0, -350.0+t*600.0, 1000.0);
ro.y -= t*0.1;
}
else if (t < 40.0) // 2. wonderful flyby over the scenery
{
lightPos = vec3(-400.0, 250.0, 1000.0);
ro.y -= 0.1;
}
else if (t < 64.0) //3. approaching the artifact
{
// Determine the scene we're in
int scene = int((t-40.0)/6.0);
if (scene >= 3)
artifactPos.w = 1.;
lightPos = vec3(-400.0, min(250., norm(rnd(vec2(scene, 2))) * 400.0), 1000.0);
// Get a random initial position for our camera
ro.xz += vec2(0.1, 20.0) * vec2(rnd(vec2(scene, 5.0)), rnd(vec2(scene, 7.0)));
// Basing on the initial position, choose some "random" start and end points nearby
ro = mix(
ro+vec3(0.008)*abs(vec3(rnd(vec2(scene, 8.0)), 0.0, rnd(vec2(scene, 10.0)))),
ro+vec3(.75) *abs(vec3(rnd(vec2(scene, 11.0)), 0.0, rnd(vec2(scene, 13.0)))),
t-40.0-float(scene));
ro.y = 0.2;
rd = rotateY(rd, rnd(vec2(scene, 14.0))*pi);
}
else // 4. artifact is in the middle and 5. BOOM
{
artifactPos.w = 1.0;
lightPos = vec3(-400.0, norm(sin(t+10.0)) * 250.0, 1000.0);
float i = smoothstep(118.,120.,t);
ro.xz = artifactPos.xz +
mix(vec2(sin(t*0.6), cos(t*0.6))*mix(sin(t*1.5+10.0)+2.0,2.,i),
vec2(0.0,2.0+0.75*(t - EXPLOSIONTIME)),
smoothstep(-1.0, 1.0, t - EXPLOSIONTIME)
);
ro.y = mix(norm(sin(t*3.0)*(1.0-i))*0.3,
0.0,
smoothstep(-1.0, 1.0, t - EXPLOSIONTIME)
) + 0.2;
vec2 dir = normalize(artifactPos.xz-ro.xz);
rd = rotateY(rd, atan(dir.y, dir.x)-pi/2.0);
}
lightDir = normalize(lightPos-ro);
float dist = length(ro-artifactPos.xyz)-1.0;
float isoDistance = artifactPos.w==1.0?traceIso(ro, rd, dist, dist+2.0, 99.0):FAR;
if (isoDistance < FAR)
{
ro += isoDistance * rd;
vec3 n = normalize(vec3(
isoSurface(vec3(ro.x-eps, ro.y, ro.z))-isoSurface(vec3(ro.x+eps, ro.y, ro.z)),
isoSurface(vec3(ro.x, ro.y-eps, ro.z))-isoSurface(vec3(ro.x, ro.y+eps, ro.z)),
isoSurface(vec3(ro.x, ro.y, ro.z-eps))-isoSurface(vec3(ro.x, ro.y, ro.z+eps))));
rd = reflect(rd, n);
}
gl_FragColor = vec4(calcScene(ro, rd) * saturate(sin((Y.z/150.0)*pi)*10.0),1.0);
}