slang-shaders/procedural/bergi-cave-quest.slang
2018-02-24 02:20:43 +01:00

333 lines
8.7 KiB
Plaintext

#version 450
// Cave Quest - Bergi - 2016-06-25
// https://www.shadertoy.com/view/XdGXD3
// Lot's of finetuning to get a nice cave fly-through.
// Greetings to Kali and Shane
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
vec4 OutputSize;
vec4 OriginalSize;
vec4 SourceSize;
uint FrameCount;
} global;
#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
const vec2 madd = vec2(0.5, 0.5);
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = gl_Position.xy;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
float iGlobalTime = float(global.FrameCount)*0.025;
vec2 iResolution = global.OutputSize.xy;
/** Cave Quest
https://www.shadertoy.com/view/XdGXD3
(cc) 2016, stefan berke
Based on "Kali Trace" https://www.shadertoy.com/view/4sKXWG
Interesting things here might be:
- the distance formula in itself
- wrapping 3d-space using sin() for endless kali-set patterns
- kali-set used for normals, micro-normals and texturing
- epsilon in normal estimation for artistic tuning
*/
// minimum distance to axis-aligned planes in kali-space
// uses eiffie's mod (/p.w) https://www.shadertoy.com/view/XtlGRj
// to keep the result close to a true distance function
vec3 kali_set(in vec3 pos, in vec3 param)
{
vec4 p = vec4(pos, 1.);
vec3 d = vec3(100.);
for (int i=0; i<9; ++i)
{
p = abs(p) / dot(p.xyz,p.xyz);
d = min(d, p.xyz/p.w);
p.xyz = p.zxy - param;
}
return d;
}
// average of all iterations in kali-set
vec3 kali_set_av(in vec3 p, in vec3 param)
{
vec3 d = vec3(0.);
for (int i=0; i<13; ++i)
{
p = abs(p) / dot(p,p);
d += exp(-p*8.);
p.xyz = p.zxy - param;
}
return d / 8.;
}
// endless texture
vec3 kali_tex(in vec3 p, in vec3 par)
{
vec3 k = kali_set_av(sin(p*3.)*.3, par);
return 3.*k;
}
// endless texture normal
vec3 kali_tex_norm(in vec3 p, in vec3 param, vec3 mask, float eps)
{
vec2 e = vec2(eps, 0.);
return normalize(vec3(
dot(kali_tex(p+e.xyy, param),mask) - dot(kali_tex(p-e.xyy, param),mask),
dot(kali_tex(p+e.yxy, param),mask) - dot(kali_tex(p-e.yxy, param),mask),
dot(kali_tex(p+e.yyx, param),mask) - dot(kali_tex(p-e.yyx, param),mask)));
}
// camera path
vec3 path(in float z)
{
float t = z;
vec3 p = vec3(sin(t)*.5,
.26*sin(t*3.16),
z);
return p;
}
float DE(in vec3 p, in vec3 param)
{
// tube around path
float r = .13+.1*sin(p.z*.89);
vec3 pp = p - path(p.z); float d = r-max(abs(pp.x), abs(pp.y));
// displacement
vec3 k = kali_set(sin(p), param);
d += k.x+k.y+k.z;
//d += max(k.x,max(k.y,k.z));
//d += min(k.x,min(k.y,k.z));
return d;
}
vec3 DE_norm(in vec3 p, in vec3 param, in float eps)
{
vec2 e = vec2(eps, 0.);
return normalize(vec3(
DE(p+e.xyy, param) - DE(p-e.xyy, param),
DE(p+e.yxy, param) - DE(p-e.yxy, param),
DE(p+e.yyx, param) - DE(p-e.yyx, param)));
}
// lighting/shading currently depends on this beeing 1.
const float max_t = 1.;
// common sphere tracing
// note the check against abs(d) to get closer to surface
// in case of overstepping
float trace(in vec3 ro, in vec3 rd, in vec3 param)
{
float t = 0.001, d = max_t;
for (int i=0; i<50; ++i)
{
vec3 p = ro + rd * t;
d = DE(p, param);
if (abs(d) <= 0.00001 || t >= max_t)
break;
t += d * .5; // above kali-distance still needs a lot of fudging
}
return t;
}
// "Enhanced Sphere Tracing"
// Benjamin Keinert(1) Henry Schäfer(1) Johann Korndörfer Urs Ganse(2) Marc Stamminger(1)
// 1 University of Erlangen-Nuremberg, 2 University of Helsinki
//
// It was a try... disabled by default (see rayColor() below)
// Just here for experimentation
// Obviously the algorithm does not like "fudging" which is needed for my distance field..
// It renders more stuff close to edges but creates a lot of artifacts elsewhere
float trace_enhanced(in vec3 ro, in vec3 rd, in vec3 param)
{
float omega = 1.2; // overstepping
float t = 0.001;
float candidate_error = 100000.;
float candidate_t = t;
float previousRadius = 0.;
float stepLength = .0;
float signedRadius;
float pixelRadius = .012;
float fudge = 0.6;
for (int i = 0; i < 50; ++i)
{
signedRadius = DE(rd*t + ro, param);
float radius = abs(signedRadius);
bool sorFail = omega > 1. && (radius + previousRadius) < stepLength;
if (sorFail)
{
stepLength -= omega * stepLength;
omega = 1.;
}
else
{
stepLength = signedRadius * omega;
}
previousRadius = radius;
float error = radius / t;
if (!sorFail && error < candidate_error)
{
candidate_t = t;
candidate_error = error;
}
if (!sorFail && error < pixelRadius || t > max_t)
break;
t += stepLength * fudge;
}
return (t > max_t || candidate_error > pixelRadius)
? max_t : candidate_t;
}
// common ambient occlusion
float traceAO(in vec3 ro, in vec3 rd, in vec3 param)
{
float a = 0., t = 0.01;
for (int i=0; i<5; ++i)
{
float d = DE(ro+t*rd, param);
a += d / t;
t += abs(d);
}
return clamp(a / 8., 0., 1.);
}
// environment map, also drawn from kaliset
vec3 skyColor(in vec3 rd)
{
//vec3 par = vec3(0.075, 0.565, .03);
vec3 par = vec3(.9, .81, .71);
vec3 c = kali_set(sin(rd*6.), par);
c = pow(min(vec3(1.), c*2.+vec3(1.,.86,.6)), 1.+114.*c);
return clamp(c, 0., 1.);
}
// trace and color
vec3 rayColor(in vec3 ro, in vec3 rd)
{
// magic params for kali-set
vec3 par1 = vec3(.9, .6+.5*sin(ro.z/50.), 1.), // scene geometry
par2 = vec3(.63, .55, .73), // normal/bump map
par3 = vec3(1.02, 0.82, 0.77); // normal/texture
#if 1
float t = trace(ro, rd, par1);
#else
float t = trace_enhanced(ro, rd, par1);
#endif
vec3 p = ro + t * rd;
float d = DE(p, par1);
vec3 col = vec3(0.);
// did ray hit?
if (d < 0.03)
{
float scr_eps = max(0.001, (t-0.1)*0.025);
// "some" texture values
vec3 kt = kali_tex(p, par3);
// surface normal
vec3 n = DE_norm(p, par1, 0.5*scr_eps), nn = n;
// normal displacement
n = normalize(n + 0.3*kali_tex_norm(p, par3+0.1*n, vec3(1), scr_eps));
n = normalize(n + 0.3*DE_norm(sin(n*3.+kt), par2, 2.*scr_eps)); // micro-bumps
// reflected ray
vec3 rrd = reflect(rd,n);
// normal towards light
vec3 ln = normalize(path(p.z+.1) - p);
// 1. - occlusion
float ao = pow(traceAO(p, n, par1), 1.+3.*t);
// surface color
vec3 col1 = .45 * (vec3(.7,1.,.4) + kali_tex(p, par3));
vec3 col2 = vec3(1.,.8,.6) + .3 * vec3(1.,.7,-.6) * kali_tex(p, par3);
vec3 k = kali_set_av(sin(p*(1.+3.*ao))*.3, par3);
vec3 surf = (.1 + .9 * ao)
//* vec3(1.);
* mix(col1, col2, min(1., pow(ao*2.2-.8*kt.x,5.)));
// desaturate
surf += .24 * (dot(surf,vec3(.3,.6,.1)) - surf);
// -- lighting --
float fres = pow(max(0., 1.-dot(rrd, n)), 1.) / (1.+2.*t);
// phong
surf += .25 * ao * max(0., dot(n, ln));
// spec
float d = max(0., dot(rrd, ln));
surf += .4 * pow(ao*1.2,5.) * (.5 * d + .7 * pow(d, 8.));
// fresnel highlight
surf += clamp((t-.06)*8., 0.,1.6) *
(.2+.8*ao) * vec3(.7,.8,1.) * fres;
// environment map
surf += .2 * (1.-fres) * ao * skyColor(rrd);
// distance fog
col = surf * pow(1.-t / max_t, 1.3);
}
return col;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 suv = fragCoord.xy / iResolution.xy;
vec2 uv = (fragCoord.xy - iResolution.xy*.5) / iResolution.y * 2.;
float ti = (iGlobalTime-14.)/8.;
vec3 ro = path(ti);
vec3 look = path(ti+.5);
float turn = (ro.x-look.x)*1.;
// lazily copied from Shane
// (except the hacky turn param)
float FOV = .7; // FOV - Field of view.
vec3 fwd = normalize(look-ro);
vec3 rgt = normalize(vec3(fwd.z, turn, -fwd.x));
vec3 up = cross(fwd, rgt);
vec3 rd = normalize(fwd + FOV*(uv.x*rgt + uv.y*up));
vec3 col = rayColor(ro, rd);
//col = skyColor(rd);
col *= pow(1.-dot(suv-.5,suv-.5)/.5, .6);
fragColor = vec4(pow(col,vec3(.8)),1.0);
}
void main(void)
{
//just some shit to wrap shadertoy's stuff
vec2 FragmentCoord = vTexCoord.xy*global.OutputSize.xy;
FragmentCoord.y = -FragmentCoord.y;
mainImage(FragColor,FragmentCoord);
}