slang-shaders/procedural/dave_hoskins-rolling-hills.slang
2018-02-24 02:20:43 +01:00

426 lines
11 KiB
Plaintext

#version 450
// Rolling Hills - 2013-07-29
// https://www.shadertoy.com/view/Xsf3zX
// Grassy fields. Ray marches a ray to a smooth hill, then enters detailed stepping through a grass like distance, erm, field.
// Now uses eiffie's 'Circle of Confusion' function for blurred ray marching into the grass.
// Thanks eiffie!
// Rolling hills. By David Hoskins, November 2013.
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// https://www.shadertoy.com/view/Xsf3zX
// v.2.00 Uses eiffie's 'Circle of Confusion' function
// for blurred ray marching into the grass.
// v.1.02 Camera aberrations.
// v.1.01 Added better grass, with wind movement.
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;
// For red/cyan 3D...
//#define STEREO
#define MOD2 vec2(3.07965, 7.4235)
float PI = 4.0*atan(1.0);
vec3 sunLight = normalize( vec3( 0.35, 0.2, 0.3 ) );
vec3 cameraPos;
vec3 sunColour = vec3(1.0, .75, .6);
const mat2 rotate2D = mat2(1.932, 1.623, -1.623, 1.952);
float gTime = 0.0;
//--------------------------------------------------------------------------
// Noise functions...
float Hash( float p )
{
vec2 p2 = fract(vec2(p) / MOD2);
p2 += dot(p2.yx, p2.xy+19.19);
return fract(p2.x * p2.y);
}
//--------------------------------------------------------------------------
float Hash(vec2 p)
{
p = fract(p / MOD2);
p += dot(p.xy, p.yx+19.19);
return fract(p.x * p.y);
}
//--------------------------------------------------------------------------
float Noise( in vec2 x )
{
vec2 p = floor(x);
vec2 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0;
float res = mix(mix( Hash(n+ 0.0), Hash(n+ 1.0),f.x),
mix( Hash(n+ 57.0), Hash(n+ 58.0),f.x),f.y);
return res;
}
vec2 Voronoi( in vec2 x )
{
vec2 p = floor( x );
vec2 f = fract( x );
float res=100.0,id;
for( int j=-1; j<=1; j++ )
for( int i=-1; i<=1; i++ )
{
vec2 b = vec2( float(i), float(j) );
vec2 r = vec2( b ) - f + Hash( p + b );
float d = dot(r,r);
if( d < res )
{
res = d;
id = Hash(p+b);
}
}
return vec2(max(.4-sqrt(res), 0.0),id);
}
//--------------------------------------------------------------------------
vec2 Terrain( in vec2 p)
{
float type = 0.0;
vec2 pos = p*0.003;
float w = 50.0;
float f = .0;
for (int i = 0; i < 3; i++)
{
f += Noise(pos) * w;
w = w * 0.62;
pos *= 2.5;
}
return vec2(f, type);
}
//--------------------------------------------------------------------------
vec2 Map(in vec3 p)
{
vec2 h = Terrain(p.xz);
return vec2(p.y - h.x, h.y);
}
//--------------------------------------------------------------------------
float FractalNoise(in vec2 xy)
{
float w = .7;
float f = 0.0;
for (int i = 0; i < 3; i++)
{
f += Noise(xy) * w;
w = w*0.6;
xy = 2.0 * xy;
}
return f;
}
//--------------------------------------------------------------------------
// Grab all sky information for a given ray from camera
vec3 GetSky(in vec3 rd)
{
float sunAmount = max( dot( rd, sunLight), 0.0 );
float v = pow(1.0-max(rd.y,0.0),6.);
vec3 sky = mix(vec3(.1, .2, .3), vec3(.32, .32, .32), v);
sky = sky + sunColour * sunAmount * sunAmount * .25;
sky = sky + sunColour * min(pow(sunAmount, 800.0)*1.5, .3);
return clamp(sky, 0.0, 1.0);
}
//--------------------------------------------------------------------------
// Merge grass into the sky background for correct fog colouring...
vec3 ApplyFog( in vec3 rgb, in float dis, in vec3 dir)
{
float fogAmount = clamp(dis*dis* 0.0000012, 0.0, 1.0);
return mix( rgb, GetSky(dir), fogAmount );
}
//--------------------------------------------------------------------------
vec3 DE(vec3 p)
{
float base = Terrain(p.xz).x - 1.9;
float height = Noise(p.xz*2.0)*.75 + Noise(p.xz)*.35 + Noise(p.xz*.5)*.2;
//p.y += height;
float y = p.y - base-height;
y = y*y;
vec2 ret = Voronoi((p.xz*2.5+sin(y*4.0+p.zx*12.3)*.12+vec2(sin(iGlobalTime*2.3+1.5*p.z),sin(iGlobalTime*3.6+1.5*p.x))*y*.5));
float f = ret.x * .6 + y * .58;
return vec3( y - f*1.4, clamp(f * 1.5, 0.0, 1.0), ret.y);
}
//--------------------------------------------------------------------------
// eiffie's code for calculating the aperture size for a given distance...
float CircleOfConfusion(float t)
{
return max(t * .04, (2.0 / iResolution.y) * (1.0+t));
}
//--------------------------------------------------------------------------
float Linstep(float a, float b, float t)
{
return clamp((t-a)/(b-a),0.,1.);
}
//--------------------------------------------------------------------------
vec3 GrassBlades(in vec3 rO, in vec3 rD, in vec3 mat, in float dist)
{
float d = 0.0;
// Only calculate cCoC once is enough here...
float rCoC = CircleOfConfusion(dist*.3);
float alpha = 0.0;
vec4 col = vec4(mat*0.15, 0.0);
for (int i = 0; i < 15; i++)
{
if (col.w > .99) break;
vec3 p = rO + rD * d;
vec3 ret = DE(p);
ret.x += .5 * rCoC;
if (ret.x < rCoC)
{
alpha = (1.0 - col.y) * Linstep(-rCoC, rCoC, -ret.x);//calculate the mix like cloud density
// Mix material with white tips for grass...
vec3 gra = mix(mat, vec3(.35, .35, min(pow(ret.z, 4.0)*35.0, .35)), pow(ret.y, 9.0)*.7) * ret.y;
col += vec4(gra * alpha, alpha);
}
d += max(ret.x * .7, .1);
}
if(col.w < .2)
col.xyz = vec3(0.1, .15, 0.05);
return col.xyz;
}
//--------------------------------------------------------------------------
// Calculate sun light...
void DoLighting(inout vec3 mat, in vec3 pos, in vec3 normal, in vec3 eyeDir, in float dis)
{
float h = dot(sunLight,normal);
mat = mat * sunColour*(max(h, 0.0)+.2);
}
//--------------------------------------------------------------------------
vec3 TerrainColour(vec3 pos, vec3 dir, vec3 normal, float dis, float type)
{
vec3 mat;
if (type == 0.0)
{
// Random colour...
mat = mix(vec3(.0,.3,.0), vec3(.2,.3,.0), Noise(pos.xz*.025));
// Random shadows...
float t = FractalNoise(pos.xz * .1)+.5;
// Do grass blade tracing...
mat = GrassBlades(pos, dir, mat, dis) * t;
DoLighting(mat, pos, normal,dir, dis);
}
mat = ApplyFog(mat, dis, dir);
return mat;
}
//--------------------------------------------------------------------------
// Home in on the surface by dividing by two and split...
float BinarySubdivision(in vec3 rO, in vec3 rD, float t, float oldT)
{
float halfwayT = 0.0;
for (int n = 0; n < 5; n++)
{
halfwayT = (oldT + t ) * .5;
if (Map(rO + halfwayT*rD).x < .05)
{
t = halfwayT;
}else
{
oldT = halfwayT;
}
}
return t;
}
//--------------------------------------------------------------------------
bool Scene(in vec3 rO, in vec3 rD, out float resT, out float type )
{
float t = 5.;
float oldT = 0.0;
float delta = 0.;
vec2 h = vec2(1.0, 1.0);
bool hit = false;
for( int j=0; j < 70; j++ )
{
vec3 p = rO + t*rD;
h = Map(p); // ...Get this position's height mapping.
// Are we inside, and close enough to fudge a hit?...
if( h.x < 0.05)
{
hit = true;
break;
}
delta = h.x + (t*0.03);
oldT = t;
t += delta;
}
type = h.y;
resT = BinarySubdivision(rO, rD, t, oldT);
return hit;
}
//--------------------------------------------------------------------------
vec3 CameraPath( float t )
{
//t = time + t;
vec2 p = vec2(200.0 * sin(3.54*t), 200.0 * cos(2.0*t) );
return vec3(p.x+55.0, 12.0+sin(t*.3)*6.5, -94.0+p.y);
}
//--------------------------------------------------------------------------
vec3 PostEffects(vec3 rgb, vec2 xy)
{
// Gamma first...
rgb = pow(rgb, vec3(0.45));
// Then...
#define CONTRAST 1.1
#define SATURATION 1.3
#define BRIGHTNESS 1.3
rgb = mix(vec3(.5), mix(vec3(dot(vec3(.2125, .7154, .0721), rgb*BRIGHTNESS)), rgb*BRIGHTNESS, SATURATION), CONTRAST);
// Vignette...
rgb *= .4+0.5*pow(40.0*xy.x*xy.y*(1.0-xy.x)*(1.0-xy.y), 0.2 );
return rgb;
}
//--------------------------------------------------------------------------
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
#ifdef MOUSE
float m = (iMouse.x/iResolution.x)*300.0;
##else
float m = (0.0/iResolution.x)*300.0;
#endif
float gTime = (iGlobalTime*5.0+m+2352.0)*.006;
vec2 xy = fragCoord.xy / iResolution.xy;
vec2 uv = (-1.0 + 2.0 * xy) * vec2(iResolution.x/iResolution.y,1.0);
vec3 camTar;
if (xy.y < .13 || xy.y >= .87)
{
// Top and bottom cine-crop - what a waste! :)
fragColor=vec4(vec4(0.0));
return;
}
#ifdef STEREO
float isCyan = mod(fragCoord.x + mod(fragCoord.y,2.0),2.0);
#endif
cameraPos = CameraPath(gTime + 0.0);
cameraPos.x -= 3.0;
camTar = CameraPath(gTime + .009);
cameraPos.y += Terrain(CameraPath(gTime + .009).xz).x;
camTar.y = cameraPos.y;
float roll = .4*sin(gTime+.5);
vec3 cw = normalize(camTar-cameraPos);
vec3 cp = vec3(sin(roll), cos(roll),0.0);
vec3 cu = cross(cw,cp);
vec3 cv = cross(cu,cw);
vec3 dir = normalize(uv.x*cu + uv.y*cv + 1.3*cw);
mat3 camMat = mat3(cu, cv, cw);
#ifdef STEREO
cameraPos += .85*cu*isCyan; // move camera to the right - the rd vector is still good
#endif
vec3 col;
float distance;
float type;
if( !Scene(cameraPos, dir, distance, type) )
{
// Missed scene, now just get the sky...
col = GetSky(dir);
}
else
{
// Get world coordinate of landscape...
vec3 pos = cameraPos + distance * dir;
// Get normal from sampling the high definition height map
// Use the distance to sample larger gaps to help stop aliasing...
vec2 p = vec2(0.1, 0.0);
vec3 nor = vec3(0.0, Terrain(pos.xz).x, 0.0);
vec3 v2 = nor-vec3(p.x, Terrain(pos.xz+p).x, 0.0);
vec3 v3 = nor-vec3(0.0, Terrain(pos.xz-p.yx).x, -p.x);
nor = cross(v2, v3);
nor = normalize(nor);
// Get the colour using all available data...
col = TerrainColour(pos, dir, nor, distance, type);
}
// bri is the brightness of sun at the centre of the camera direction.
// Yeah, the lens flares is not exactly subtle, but it was good fun making it.
float bri = dot(cw, sunLight)*.75;
if (bri > 0.0)
{
vec2 sunPos = vec2( dot( sunLight, cu ), dot( sunLight, cv ) );
vec2 uvT = uv-sunPos;
uvT = uvT*(length(uvT));
bri = pow(bri, 6.0)*.8;
// glare = the red shifted blob...
float glare1 = max(dot(normalize(vec3(dir.x, dir.y+.3, dir.z)),sunLight),0.0)*1.4;
// glare2 is the yellow ring...
float glare2 = max(1.0-length(uvT+sunPos*.5)*4.0, 0.0);
uvT = mix (uvT, uv, -2.3);
// glare3 is a purple splodge...
float glare3 = max(1.0-length(uvT+sunPos*5.0)*1.2, 0.0);
col += bri * vec3(1.0, .0, .0) * pow(glare1, 12.5)*.05;
col += bri * vec3(1.0, 1.0, 0.2) * pow(glare2, 2.0)*2.5;
col += bri * sunColour * pow(glare3, 2.0)*3.0;
}
col = PostEffects(col, xy);
#ifdef STEREO
col *= vec3( isCyan, 1.0-isCyan, 1.0-isCyan );
#endif
fragColor=vec4(col,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);
}