1443 lines
44 KiB
GLSL
1443 lines
44 KiB
GLSL
// Planet Shadertoy. Created by Reinder Nijhoff 2015
|
|
// @reindernijhoff
|
|
//
|
|
// https://www.shadertoy.com/view/4tjGRh
|
|
//
|
|
|
|
//#define HIGH_QUALITY
|
|
//#define MED_QUALITY
|
|
//#define LOW_QUALITY
|
|
#define VERY_LOW_QUALITY
|
|
|
|
const float PI = 3.14159265359;
|
|
const float DEG_TO_RAD = (PI / 180.0);
|
|
const float MAX = 10000.0;
|
|
|
|
const float EARTH_RADIUS = 1000.;
|
|
const float EARTH_ATMOSPHERE = 5.;
|
|
const float EARTH_CLOUDS = 1.;
|
|
|
|
const float RING_INNER_RADIUS = 1500.;
|
|
const float RING_OUTER_RADIUS = 2300.;
|
|
const float RING_HEIGHT = 2.;
|
|
|
|
#ifdef HIGH_QUALITY
|
|
const int SEA_NUM_STEPS = 7;
|
|
const int TERRAIN_NUM_STEPS = 140;
|
|
const int ASTEROID_NUM_STEPS = 11;
|
|
const int ASTEROID_NUM_BOOL_SUB = 7;
|
|
const int RING_VOXEL_STEPS = 25;
|
|
const float ASTEROID_MAX_DISTANCE = 1.1;
|
|
const int FBM_STEPS = 4;
|
|
const int ATMOSPHERE_NUM_OUT_SCATTER = 5;
|
|
const int ATMOSPHERE_NUM_IN_SCATTER = 7;
|
|
|
|
#define DISPLAY_LLAMEL
|
|
#define DISPLAY_CLOUDS
|
|
#define DISPLAY_CLOUDS_DETAIL
|
|
#define DISPLAY_TERRAIN_DETAIL
|
|
#endif
|
|
|
|
#ifdef MED_QUALITY
|
|
const int SEA_NUM_STEPS = 6;
|
|
const int TERRAIN_NUM_STEPS = 100;
|
|
const int ASTEROID_NUM_STEPS = 10;
|
|
const int ASTEROID_NUM_BOOL_SUB = 6;
|
|
const int RING_VOXEL_STEPS = 24;
|
|
const float ASTEROID_MAX_DISTANCE = 1.;
|
|
const int FBM_STEPS = 4;
|
|
const int ATMOSPHERE_NUM_OUT_SCATTER = 4;
|
|
const int ATMOSPHERE_NUM_IN_SCATTER = 6;
|
|
#define DISPLAY_CLOUDS
|
|
#define DISPLAY_TERRAIN_DETAIL
|
|
#define DISPLAY_CLOUDS_DETAIL
|
|
#endif
|
|
|
|
#ifdef LOW_QUALITY
|
|
const int SEA_NUM_STEPS = 5;
|
|
const int TERRAIN_NUM_STEPS = 75;
|
|
const int ASTEROID_NUM_STEPS = 9;
|
|
const int ASTEROID_NUM_BOOL_SUB = 5;
|
|
const int RING_VOXEL_STEPS = 20;
|
|
const float ASTEROID_MAX_DISTANCE = .85;
|
|
const int FBM_STEPS = 3;
|
|
const int ATMOSPHERE_NUM_OUT_SCATTER = 3;
|
|
const int ATMOSPHERE_NUM_IN_SCATTER = 5;
|
|
#endif
|
|
|
|
#ifdef VERY_LOW_QUALITY
|
|
const int SEA_NUM_STEPS = 4;
|
|
const int TERRAIN_NUM_STEPS = 60;
|
|
const int ASTEROID_NUM_STEPS = 7;
|
|
const int ASTEROID_NUM_BOOL_SUB = 4;
|
|
const int RING_VOXEL_STEPS = 16;
|
|
const float ASTEROID_MAX_DISTANCE = .67;
|
|
const int FBM_STEPS = 3;
|
|
const int ATMOSPHERE_NUM_OUT_SCATTER = 2;
|
|
const int ATMOSPHERE_NUM_IN_SCATTER = 4;
|
|
#define HIDE_TERRAIN
|
|
#endif
|
|
|
|
const vec3 SUN_DIRECTION = vec3( .940721, .28221626, .18814417 );
|
|
const vec3 SUN_COLOR = vec3(.3, .21, .165);
|
|
|
|
float time;
|
|
|
|
//-----------------------------------------------------
|
|
// Noise functions
|
|
//-----------------------------------------------------
|
|
|
|
float hash( const in float n ) {
|
|
return fract(sin(n)*43758.5453123);
|
|
}
|
|
float hash( const in vec2 p ) {
|
|
float h = dot(p,vec2(127.1,311.7));
|
|
return fract(sin(h)*43758.5453123);
|
|
}
|
|
float hash( const in vec3 p ) {
|
|
float h = dot(p,vec3(127.1,311.7,758.5453123));
|
|
return fract(sin(h)*43758.5453123);
|
|
}
|
|
vec3 hash31( const in float p) {
|
|
vec3 h = vec3(1275.231,4461.7,7182.423) * p;
|
|
return fract(sin(h)*43758.543123);
|
|
}
|
|
vec3 hash33( const in vec3 p) {
|
|
return vec3( hash(p), hash(p.zyx), hash(p.yxz) );
|
|
}
|
|
|
|
float noise( const in float p ) {
|
|
float i = floor( p );
|
|
float f = fract( p );
|
|
float u = f*f*(3.0-2.0*f);
|
|
return -1.0+2.0* mix( hash( i + 0. ), hash( i + 1. ), u);
|
|
}
|
|
|
|
float noise( const in vec2 p ) {
|
|
vec2 i = floor( p );
|
|
vec2 f = fract( p );
|
|
vec2 u = f*f*(3.0-2.0*f);
|
|
return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),
|
|
hash( i + vec2(1.0,0.0) ), u.x),
|
|
mix( hash( i + vec2(0.0,1.0) ),
|
|
hash( i + vec2(1.0,1.0) ), u.x), u.y);
|
|
}
|
|
float noise( const in vec3 x ) {
|
|
vec3 p = floor(x);
|
|
vec3 f = fract(x);
|
|
f = f*f*(3.0-2.0*f);
|
|
float n = p.x + p.y*157.0 + 113.0*p.z;
|
|
return mix(mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x),
|
|
mix( hash(n+157.0), hash(n+158.0),f.x),f.y),
|
|
mix(mix( hash(n+113.0), hash(n+114.0),f.x),
|
|
mix( hash(n+270.0), hash(n+271.0),f.x),f.y),f.z);
|
|
}
|
|
|
|
float tri( const in vec2 p ) {
|
|
return 0.5*(cos(6.2831*p.x) + cos(6.2831*p.y));
|
|
|
|
}
|
|
|
|
const mat2 m2 = mat2( 0.80, -0.60, 0.60, 0.80 );
|
|
|
|
float fbm( in vec2 p ) {
|
|
float f = 0.0;
|
|
f += 0.5000*noise( p ); p = m2*p*2.02;
|
|
f += 0.2500*noise( p ); p = m2*p*2.03;
|
|
f += 0.1250*noise( p );
|
|
|
|
#ifndef LOW_QUALITY
|
|
#ifndef VERY_LOW_QUALITY
|
|
p = m2*p*2.01;
|
|
f += 0.0625*noise( p );
|
|
#endif
|
|
#endif
|
|
return f/0.9375;
|
|
}
|
|
|
|
float fbm( const in vec3 p, const in float a, const in float f) {
|
|
float ret = 0.0;
|
|
float amp = 1.0;
|
|
float frq = 1.0;
|
|
for(int i = 0; i < FBM_STEPS; i++) {
|
|
float n = pow(noise(p * frq),2.0);
|
|
ret += n * amp;
|
|
frq *= f;
|
|
amp *= a * (pow(n,0.2));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Lightning functions
|
|
//-----------------------------------------------------
|
|
|
|
float diffuse( const in vec3 n, const in vec3 l) {
|
|
return clamp(dot(n,l),0.,1.);
|
|
}
|
|
|
|
float specular( const in vec3 n, const in vec3 l, const in vec3 e, const in float s) {
|
|
float nrm = (s + 8.0) / (3.1415 * 8.0);
|
|
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
|
|
}
|
|
|
|
float fresnel( const in vec3 n, const in vec3 e, float s ) {
|
|
return pow(clamp(1.-dot(n,e), 0., 1.),s);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Math functions
|
|
//-----------------------------------------------------
|
|
|
|
vec2 rotate(float angle, vec2 v) {
|
|
return vec2(cos(angle) * v.x + sin(angle) * v.y, cos(angle) * v.y - sin(angle) * v.x);
|
|
}
|
|
|
|
float boolSub(float a,float b) {
|
|
return max(a,-b);
|
|
}
|
|
float sphere(vec3 p,float r) {
|
|
return length(p)-r;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Intersection functions (by iq)
|
|
//-----------------------------------------------------
|
|
|
|
vec3 nSphere( in vec3 pos, in vec4 sph ) {
|
|
return (pos-sph.xyz)/sph.w;
|
|
}
|
|
|
|
float iSphere( in vec3 ro, in vec3 rd, in vec4 sph ) {
|
|
vec3 oc = ro - sph.xyz;
|
|
float b = dot( oc, rd );
|
|
float c = dot( oc, oc ) - sph.w*sph.w;
|
|
float h = b*b - c;
|
|
if( h<0.0 ) return -1.0;
|
|
return -b - sqrt( h );
|
|
}
|
|
|
|
float iCSphereF( vec3 p, vec3 dir, float r ) {
|
|
float b = dot( p, dir );
|
|
float c = dot( p, p ) - r * r;
|
|
float d = b * b - c;
|
|
if ( d < 0.0 ) return -MAX;
|
|
return -b + sqrt( d );
|
|
}
|
|
|
|
vec2 iCSphere2( vec3 p, vec3 dir, float r ) {
|
|
float b = dot( p, dir );
|
|
float c = dot( p, p ) - r * r;
|
|
float d = b * b - c;
|
|
if ( d < 0.0 ) return vec2( MAX, -MAX );
|
|
d = sqrt( d );
|
|
return vec2( -b - d, -b + d );
|
|
}
|
|
|
|
vec3 nPlane( in vec3 ro, in vec4 obj ) {
|
|
return obj.xyz;
|
|
}
|
|
|
|
float iPlane( in vec3 ro, in vec3 rd, in vec4 pla ) {
|
|
return (-pla.w - dot(pla.xyz,ro)) / dot( pla.xyz, rd );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Wet stone by TDM
|
|
//
|
|
// https://www.shadertoy.com/view/ldSSzV
|
|
//-----------------------------------------------------
|
|
|
|
const float ASTEROID_TRESHOLD = 0.001;
|
|
const float ASTEROID_EPSILON = 1e-6;
|
|
const float ASTEROID_DISPLACEMENT = 0.1;
|
|
const float ASTEROID_RADIUS = 0.13;
|
|
|
|
const vec3 RING_COLOR_1 = vec3(0.42,0.3,0.2);
|
|
const vec3 RING_COLOR_2 = vec3(0.51,0.41,0.32) * 0.2;
|
|
|
|
float asteroidRock( const in vec3 p, const in vec3 id ) {
|
|
float d = sphere(p,ASTEROID_RADIUS);
|
|
for(int i = 0; i < ASTEROID_NUM_BOOL_SUB; i++) {
|
|
float ii = float(i)+id.x;
|
|
float r = (ASTEROID_RADIUS*2.5) + ASTEROID_RADIUS*hash(ii);
|
|
vec3 v = normalize(hash31(ii) * 2.0 - 1.0);
|
|
d = boolSub(d,sphere(p+v*r,r * 0.8));
|
|
}
|
|
return d;
|
|
}
|
|
|
|
float asteroidMap( const in vec3 p, const in vec3 id) {
|
|
float d = asteroidRock(p, id) + noise(p*4.0) * ASTEROID_DISPLACEMENT;
|
|
return d;
|
|
}
|
|
|
|
float asteroidMapDetailed( const in vec3 p, const in vec3 id) {
|
|
float d = asteroidRock(p, id) + fbm(p*4.0,0.4,2.96) * ASTEROID_DISPLACEMENT;
|
|
return d;
|
|
}
|
|
|
|
void asteroidTransForm(inout vec3 ro, const in vec3 id ) {
|
|
float xyangle = (id.x-.5)*time*2.;
|
|
ro.xy = rotate( xyangle, ro.xy );
|
|
|
|
float yzangle = (id.y-.5)*time*2.;
|
|
ro.yz = rotate( yzangle, ro.yz );
|
|
}
|
|
|
|
void asteroidUnTransForm(inout vec3 ro, const in vec3 id ) {
|
|
float yzangle = (id.y-.5)*time*2.;
|
|
ro.yz = rotate( -yzangle, ro.yz );
|
|
|
|
float xyangle = (id.x-.5)*time*2.;
|
|
ro.xy = rotate( -xyangle, ro.xy );
|
|
}
|
|
|
|
vec3 asteroidGetNormal(vec3 p, vec3 id) {
|
|
asteroidTransForm( p, id );
|
|
|
|
vec3 n;
|
|
n.x = asteroidMapDetailed(vec3(p.x+ASTEROID_EPSILON,p.y,p.z), id);
|
|
n.y = asteroidMapDetailed(vec3(p.x,p.y+ASTEROID_EPSILON,p.z), id);
|
|
n.z = asteroidMapDetailed(vec3(p.x,p.y,p.z+ASTEROID_EPSILON), id);
|
|
n = normalize(n-asteroidMapDetailed(p, id));
|
|
|
|
asteroidUnTransForm( n, id );
|
|
return n;
|
|
}
|
|
|
|
vec2 asteroidSpheretracing(vec3 ori, vec3 dir, vec3 id) {
|
|
asteroidTransForm( ori, id );
|
|
asteroidTransForm( dir, id );
|
|
|
|
vec2 td = vec2(0.0);
|
|
for(int i = 0; i < ASTEROID_NUM_STEPS; i++) {
|
|
vec3 p = ori + dir * td.x;
|
|
td.y = asteroidMap(p, id);
|
|
if(td.y < ASTEROID_TRESHOLD) break;
|
|
td.x += (td.y-ASTEROID_TRESHOLD) * 0.9;
|
|
}
|
|
return td;
|
|
}
|
|
|
|
vec3 asteroidGetStoneColor(vec3 p, float c, vec3 l, vec3 n, vec3 e) {
|
|
return mix( diffuse(n,l)*RING_COLOR_1*SUN_COLOR, SUN_COLOR*specular(n,l,e,3.0), .5*fresnel(n,e,5.));
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Ring (by me ;))
|
|
//-----------------------------------------------------
|
|
|
|
const float RING_DETAIL_DISTANCE = 40.;
|
|
const float RING_VOXEL_STEP_SIZE = .03;
|
|
|
|
vec3 ringShadowColor( const in vec3 ro ) {
|
|
if( iSphere( ro, SUN_DIRECTION, vec4( 0., 0., 0., EARTH_RADIUS ) ) > 0. ) {
|
|
return vec3(0.);
|
|
}
|
|
return vec3(1.);
|
|
}
|
|
|
|
bool ringMap( const in vec3 ro ) {
|
|
return ro.z < RING_HEIGHT/RING_VOXEL_STEP_SIZE && hash(ro)<.5;
|
|
}
|
|
|
|
vec4 renderRingNear( const in vec3 ro, const in vec3 rd ) {
|
|
// find startpoint
|
|
float d1 = iPlane( ro, rd, vec4( 0., 0., 1., RING_HEIGHT ) );
|
|
float d2 = iPlane( ro, rd, vec4( 0., 0., 1., -RING_HEIGHT ) );
|
|
|
|
if( d1 < 0. && d2 < 0. ) return vec4( 0. );
|
|
|
|
float d = min( max(d1,0.), max(d2,0.) );
|
|
|
|
if( d > ASTEROID_MAX_DISTANCE ) return vec4( 0. );
|
|
|
|
vec3 ros = ro + rd*d;
|
|
|
|
// avoid precision problems..
|
|
vec2 mroxy = mod(ros.xy, vec2(10.));
|
|
vec2 roxy = ros.xy - mroxy;
|
|
ros.xy -= roxy;
|
|
ros /= RING_VOXEL_STEP_SIZE;
|
|
ros.xy -= vec2(.013,.112)*time*.5;
|
|
|
|
vec3 pos = floor(ros);
|
|
vec3 ri = 1.0/rd;
|
|
vec3 rs = sign(rd);
|
|
vec3 dis = (pos-ros + 0.5 + rs*0.5) * ri;
|
|
|
|
float alpha = 0., dint;
|
|
vec3 offset = vec3(0.), id, asteroidro;
|
|
vec2 asteroid;
|
|
|
|
for( int i=0; i<RING_VOXEL_STEPS; i++ ) {
|
|
if( ringMap(pos) ) {
|
|
id = hash33(pos);
|
|
offset = id*(1.-2.*ASTEROID_RADIUS)+ASTEROID_RADIUS;
|
|
dint = iSphere( ros, rd, vec4(pos+offset, ASTEROID_RADIUS) );
|
|
|
|
if( dint > 0. ) {
|
|
asteroidro = ros+rd*dint-(pos+offset);
|
|
asteroid = asteroidSpheretracing( asteroidro, rd, id );
|
|
|
|
if( asteroid.y < .1 ) {
|
|
alpha = 1.;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
vec3 mm = step(dis.xyz, dis.yxy) * step(dis.xyz, dis.zzx);
|
|
dis += mm * rs * ri;
|
|
pos += mm * rs;
|
|
}
|
|
|
|
if( alpha > 0. ) {
|
|
vec3 intersection = ros + rd*(asteroid.x+dint);
|
|
vec3 n = asteroidGetNormal( asteroidro + rd*asteroid.x, id );
|
|
|
|
vec3 col = asteroidGetStoneColor(intersection, .1, SUN_DIRECTION, n, rd);
|
|
|
|
intersection *= RING_VOXEL_STEP_SIZE;
|
|
intersection.xy += roxy;
|
|
col *= ringShadowColor( intersection );
|
|
|
|
return vec4( col, 1.-smoothstep(0.4*ASTEROID_MAX_DISTANCE, 0.5* ASTEROID_MAX_DISTANCE, distance( intersection, ro ) ) );
|
|
}
|
|
|
|
return vec4(0.);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Ring (by me ;))
|
|
//-----------------------------------------------------
|
|
|
|
float renderRingFarShadow( const in vec3 ro, const in vec3 rd ) {
|
|
// intersect plane
|
|
float d = iPlane( ro, rd, vec4( 0., 0., 1., 0.) );
|
|
|
|
if( d > 0. ) {
|
|
vec3 intersection = ro + rd*d;
|
|
float l = length(intersection.xy);
|
|
|
|
if( l > RING_INNER_RADIUS && l < RING_OUTER_RADIUS ) {
|
|
return .5 + .5 * (.2+.8*noise( l*.07 )) * (.5+.5*noise(intersection.xy));
|
|
}
|
|
}
|
|
return 0.;
|
|
}
|
|
|
|
vec4 renderRingFar( const in vec3 ro, const in vec3 rd, inout float maxd ) {
|
|
// intersect plane
|
|
float d = iPlane( ro, rd, vec4( 0., 0., 1., 0.) );
|
|
|
|
if( d > 0. && d < maxd ) {
|
|
maxd = d;
|
|
vec3 intersection = ro + rd*d;
|
|
float l = length(intersection.xy);
|
|
|
|
if( l > RING_INNER_RADIUS && l < RING_OUTER_RADIUS ) {
|
|
float dens = .5 + .5 * (.2+.8*noise( l*.07 )) * (.5+.5*noise(intersection.xy));
|
|
vec3 col = mix( RING_COLOR_1, RING_COLOR_2, abs( noise(l*0.2) ) ) * abs(dens) * 1.5;
|
|
|
|
col *= ringShadowColor( intersection );
|
|
col *= .8+.3*diffuse( vec3(0,0,1), SUN_DIRECTION );
|
|
col *= SUN_COLOR;
|
|
return vec4( col, dens );
|
|
}
|
|
}
|
|
return vec4(0.);
|
|
}
|
|
|
|
vec4 renderRing( const in vec3 ro, const in vec3 rd, inout float maxd ) {
|
|
vec4 far = renderRingFar( ro, rd, maxd );
|
|
float l = length( ro.xy );
|
|
|
|
if( abs(ro.z) < RING_HEIGHT+RING_DETAIL_DISTANCE
|
|
&& l < RING_OUTER_RADIUS+RING_DETAIL_DISTANCE
|
|
&& l > RING_INNER_RADIUS-RING_DETAIL_DISTANCE ) {
|
|
|
|
float d = iPlane( ro, rd, vec4( 0., 0., 1., 0.) );
|
|
float detail = mix( .5 * noise( fract(ro.xy+rd.xy*d) * 92.1)+.25, 1., smoothstep( 0.,RING_DETAIL_DISTANCE, d) );
|
|
far.xyz *= detail;
|
|
}
|
|
|
|
// are asteroids neaded ?
|
|
if( abs(ro.z) < RING_HEIGHT+ASTEROID_MAX_DISTANCE
|
|
&& l < RING_OUTER_RADIUS+ASTEROID_MAX_DISTANCE
|
|
&& l > RING_INNER_RADIUS-ASTEROID_MAX_DISTANCE ) {
|
|
|
|
vec4 near = renderRingNear( ro, rd );
|
|
far = mix( far, near, near.w );
|
|
maxd=0.;
|
|
}
|
|
|
|
return far;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Stars (by me ;))
|
|
//-----------------------------------------------------
|
|
|
|
vec4 renderStars( const in vec3 rd ) {
|
|
vec3 rds = rd;
|
|
vec3 col = vec3(0);
|
|
float v = 1.0/( 2. * ( 1. + rds.z ) );
|
|
|
|
vec2 xy = vec2(rds.y * v, rds.x * v);
|
|
float s = noise(rds*134.);
|
|
|
|
s += noise(rds*470.);
|
|
s = pow(s,19.0) * 0.00001;
|
|
if (s > 0.5) {
|
|
vec3 backStars = vec3(s)*.5 * vec3(0.95,0.8,0.9);
|
|
col += backStars;
|
|
}
|
|
return vec4( col, 1 );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Atmospheric Scattering by GLtracy
|
|
//
|
|
// https://www.shadertoy.com/view/lslXDr
|
|
//-----------------------------------------------------
|
|
|
|
const float ATMOSPHERE_K_R = 0.166;
|
|
const float ATMOSPHERE_K_M = 0.0025;
|
|
const float ATMOSPHERE_E = 12.3;
|
|
const vec3 ATMOSPHERE_C_R = vec3( 0.3, 0.7, 1.0 );
|
|
const float ATMOSPHERE_G_M = -0.85;
|
|
|
|
const float ATMOSPHERE_SCALE_H = 4.0 / ( EARTH_ATMOSPHERE );
|
|
const float ATMOSPHERE_SCALE_L = 1.0 / ( EARTH_ATMOSPHERE );
|
|
|
|
const float ATMOSPHERE_FNUM_OUT_SCATTER = float(ATMOSPHERE_NUM_OUT_SCATTER);
|
|
const float ATMOSPHERE_FNUM_IN_SCATTER = float(ATMOSPHERE_NUM_IN_SCATTER);
|
|
|
|
const int ATMOSPHERE_NUM_OUT_SCATTER_LOW = 2;
|
|
const int ATMOSPHERE_NUM_IN_SCATTER_LOW = 4;
|
|
const float ATMOSPHERE_FNUM_OUT_SCATTER_LOW = float(ATMOSPHERE_NUM_OUT_SCATTER_LOW);
|
|
const float ATMOSPHERE_FNUM_IN_SCATTER_LOW = float(ATMOSPHERE_NUM_IN_SCATTER_LOW);
|
|
|
|
float atmosphericPhaseMie( float g, float c, float cc ) {
|
|
float gg = g * g;
|
|
float a = ( 1.0 - gg ) * ( 1.0 + cc );
|
|
float b = 1.0 + gg - 2.0 * g * c;
|
|
|
|
b *= sqrt( b );
|
|
b *= 2.0 + gg;
|
|
|
|
return 1.5 * a / b;
|
|
}
|
|
|
|
float atmosphericPhaseReyleigh( float cc ) {
|
|
return 0.75 * ( 1.0 + cc );
|
|
}
|
|
|
|
float atmosphericDensity( vec3 p ){
|
|
return exp( -( length( p ) - EARTH_RADIUS ) * ATMOSPHERE_SCALE_H );
|
|
}
|
|
|
|
float atmosphericOptic( vec3 p, vec3 q ) {
|
|
vec3 step = ( q - p ) / ATMOSPHERE_FNUM_OUT_SCATTER;
|
|
vec3 v = p + step * 0.5;
|
|
|
|
float sum = 0.0;
|
|
for ( int i = 0; i < ATMOSPHERE_NUM_OUT_SCATTER; i++ ) {
|
|
sum += atmosphericDensity( v );
|
|
v += step;
|
|
}
|
|
sum *= length( step ) * ATMOSPHERE_SCALE_L;
|
|
|
|
return sum;
|
|
}
|
|
|
|
vec4 atmosphericInScatter( vec3 o, vec3 dir, vec2 e, vec3 l ) {
|
|
float len = ( e.y - e.x ) / ATMOSPHERE_FNUM_IN_SCATTER;
|
|
vec3 step = dir * len;
|
|
vec3 p = o + dir * e.x;
|
|
vec3 v = p + dir * ( len * 0.5 );
|
|
|
|
float sumdensity = 0.;
|
|
vec3 sum = vec3( 0.0 );
|
|
|
|
for ( int i = 0; i < ATMOSPHERE_NUM_IN_SCATTER; i++ ) {
|
|
vec3 u = v + l * iCSphereF( v, l, EARTH_RADIUS + EARTH_ATMOSPHERE );
|
|
float n = ( atmosphericOptic( p, v ) + atmosphericOptic( v, u ) ) * ( PI * 4.0 );
|
|
float dens = atmosphericDensity( v );
|
|
|
|
float m = MAX;
|
|
sum += dens * exp( -n * ( ATMOSPHERE_K_R * ATMOSPHERE_C_R + ATMOSPHERE_K_M ) )
|
|
* (1. - renderRingFarShadow( u, SUN_DIRECTION ) );
|
|
sumdensity += dens;
|
|
|
|
v += step;
|
|
}
|
|
sum *= len * ATMOSPHERE_SCALE_L;
|
|
|
|
float c = dot( dir, -l );
|
|
float cc = c * c;
|
|
|
|
return vec4( sum * ( ATMOSPHERE_K_R * ATMOSPHERE_C_R * atmosphericPhaseReyleigh( cc ) +
|
|
ATMOSPHERE_K_M * atmosphericPhaseMie( ATMOSPHERE_G_M, c, cc ) ) * ATMOSPHERE_E,
|
|
clamp(sumdensity * len * ATMOSPHERE_SCALE_L,0.,1.));
|
|
}
|
|
|
|
float atmosphericOpticLow( vec3 p, vec3 q ) {
|
|
vec3 step = ( q - p ) / ATMOSPHERE_FNUM_OUT_SCATTER_LOW;
|
|
vec3 v = p + step * 0.5;
|
|
|
|
float sum = 0.0;
|
|
for ( int i = 0; i < ATMOSPHERE_NUM_OUT_SCATTER_LOW; i++ ) {
|
|
sum += atmosphericDensity( v );
|
|
v += step;
|
|
}
|
|
sum *= length( step ) * ATMOSPHERE_SCALE_L;
|
|
|
|
return sum;
|
|
}
|
|
|
|
vec3 atmosphericInScatterLow( vec3 o, vec3 dir, vec2 e, vec3 l ) {
|
|
float len = ( e.y - e.x ) / ATMOSPHERE_FNUM_IN_SCATTER_LOW;
|
|
vec3 step = dir * len;
|
|
vec3 p = o + dir * e.x;
|
|
vec3 v = p + dir * ( len * 0.5 );
|
|
|
|
vec3 sum = vec3( 0.0 );
|
|
|
|
for ( int i = 0; i < ATMOSPHERE_NUM_IN_SCATTER_LOW; i++ ) {
|
|
vec3 u = v + l * iCSphereF( v, l, EARTH_RADIUS + EARTH_ATMOSPHERE );
|
|
float n = ( atmosphericOpticLow( p, v ) + atmosphericOpticLow( v, u ) ) * ( PI * 4.0 );
|
|
float m = MAX;
|
|
sum += atmosphericDensity( v ) * exp( -n * ( ATMOSPHERE_K_R * ATMOSPHERE_C_R + ATMOSPHERE_K_M ) );
|
|
v += step;
|
|
}
|
|
sum *= len * ATMOSPHERE_SCALE_L;
|
|
|
|
float c = dot( dir, -l );
|
|
float cc = c * c;
|
|
|
|
return sum * ( ATMOSPHERE_K_R * ATMOSPHERE_C_R * atmosphericPhaseReyleigh( cc ) +
|
|
ATMOSPHERE_K_M * atmosphericPhaseMie( ATMOSPHERE_G_M, c, cc ) ) * ATMOSPHERE_E;
|
|
}
|
|
|
|
vec4 renderAtmospheric( const in vec3 ro, const in vec3 rd, inout float d ) {
|
|
// inside or outside atmosphere?
|
|
vec2 e = iCSphere2( ro, rd, EARTH_RADIUS + EARTH_ATMOSPHERE );
|
|
vec2 f = iCSphere2( ro, rd, EARTH_RADIUS );
|
|
|
|
if( length(ro) <= EARTH_RADIUS + EARTH_ATMOSPHERE ) {
|
|
if( d < e.y ) {
|
|
e.y = d;
|
|
}
|
|
d = e.y;
|
|
e.x = 0.;
|
|
|
|
if ( iSphere( ro, rd, vec4(0,0,0,EARTH_RADIUS)) > 0. ) {
|
|
d = iSphere( ro, rd, vec4(0,0,0,EARTH_RADIUS));
|
|
}
|
|
} else {
|
|
if( iSphere( ro, rd, vec4(0,0,0,EARTH_RADIUS + EARTH_ATMOSPHERE )) < 0. ) return vec4(0.);
|
|
|
|
if ( e.x > e.y ) {
|
|
d = MAX;
|
|
return vec4(0.);
|
|
}
|
|
d = e.y = min( e.y, f.x );
|
|
}
|
|
return atmosphericInScatter( ro, rd, e, SUN_DIRECTION );
|
|
}
|
|
|
|
vec3 renderAtmosphericLow( const in vec3 ro, const in vec3 rd ) {
|
|
vec2 e = iCSphere2( ro, rd, EARTH_RADIUS + EARTH_ATMOSPHERE );
|
|
e.x = 0.;
|
|
return atmosphericInScatterLow( ro, rd, e, SUN_DIRECTION );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Seascape by TDM
|
|
//
|
|
// https://www.shadertoy.com/view/Ms2SD1
|
|
//-----------------------------------------------------
|
|
|
|
const int SEA_ITER_GEOMETRY = 3;
|
|
const int SEA_ITER_FRAGMENT = 5;
|
|
|
|
const float SEA_EPSILON = 1e-3;
|
|
#define SEA_EPSILON_NRM (0.1 / iResolution.x)
|
|
const float SEA_HEIGHT = 0.6;
|
|
const float SEA_CHOPPY = 4.0;
|
|
const float SEA_SPEED = 0.8;
|
|
const float SEA_FREQ = 0.16;
|
|
const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
|
|
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
|
|
float SEA_TIME;
|
|
const mat2 sea_octave_m = mat2(1.6,1.2,-1.2,1.6);
|
|
|
|
float seaOctave( in vec2 uv, const in float choppy) {
|
|
uv += noise(uv);
|
|
vec2 wv = 1.0-abs(sin(uv));
|
|
vec2 swv = abs(cos(uv));
|
|
wv = mix(wv,swv,wv);
|
|
return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
|
|
}
|
|
|
|
float seaMap(const in vec3 p) {
|
|
float freq = SEA_FREQ;
|
|
float amp = SEA_HEIGHT;
|
|
float choppy = SEA_CHOPPY;
|
|
vec2 uv = p.xz; uv.x *= 0.75;
|
|
|
|
float d, h = 0.0;
|
|
for(int i = 0; i < SEA_ITER_GEOMETRY; i++) {
|
|
d = seaOctave((uv+SEA_TIME)*freq,choppy);
|
|
d += seaOctave((uv-SEA_TIME)*freq,choppy);
|
|
h += d * amp;
|
|
uv *= sea_octave_m; freq *= 1.9; amp *= 0.22;
|
|
choppy = mix(choppy,1.0,0.2);
|
|
}
|
|
return p.y - h;
|
|
}
|
|
|
|
float seaMapHigh(const in vec3 p) {
|
|
float freq = SEA_FREQ;
|
|
float amp = SEA_HEIGHT;
|
|
float choppy = SEA_CHOPPY;
|
|
vec2 uv = p.xz; uv.x *= 0.75;
|
|
|
|
float d, h = 0.0;
|
|
for(int i = 0; i < SEA_ITER_FRAGMENT; i++) {
|
|
d = seaOctave((uv+SEA_TIME)*freq,choppy);
|
|
d += seaOctave((uv-SEA_TIME)*freq,choppy);
|
|
h += d * amp;
|
|
uv *= sea_octave_m; freq *= 1.9; amp *= 0.22;
|
|
choppy = mix(choppy,1.0,0.2);
|
|
}
|
|
return p.y - h;
|
|
}
|
|
|
|
vec3 seaGetColor( const in vec3 n, vec3 eye, const in vec3 l, const in float att,
|
|
const in vec3 sunc, const in vec3 upc, const in vec3 reflected) {
|
|
vec3 refracted = SEA_BASE * upc + diffuse(n,l) * SEA_WATER_COLOR * 0.12 * sunc;
|
|
vec3 color = mix(refracted,reflected,fresnel(n, -eye, 3.)*.65 );
|
|
|
|
color += upc*SEA_WATER_COLOR * (att * 0.18);
|
|
color += sunc * vec3(specular(n,l,eye,60.0));
|
|
|
|
return color;
|
|
}
|
|
|
|
vec3 seaGetNormal(const in vec3 p, const in float eps) {
|
|
vec3 n;
|
|
n.y = seaMapHigh(p);
|
|
n.x = seaMapHigh(vec3(p.x+eps,p.y,p.z)) - n.y;
|
|
n.z = seaMapHigh(vec3(p.x,p.y,p.z+eps)) - n.y;
|
|
n.y = eps;
|
|
return normalize(n);
|
|
}
|
|
|
|
float seaHeightMapTracing(const in vec3 ori, const in vec3 dir, out vec3 p) {
|
|
float tm = 0.0;
|
|
float tx = 1000.0;
|
|
float hx = seaMap(ori + dir * tx);
|
|
if(hx > 0.0) return tx;
|
|
float hm = seaMap(ori + dir * tm);
|
|
float tmid = 0.0;
|
|
for(int i = 0; i < SEA_NUM_STEPS; i++) {
|
|
tmid = mix(tm,tx, hm/(hm-hx));
|
|
p = ori + dir * tmid;
|
|
float hmid = seaMap(p);
|
|
if(hmid < 0.0) {
|
|
tx = tmid;
|
|
hx = hmid;
|
|
} else {
|
|
tm = tmid;
|
|
hm = hmid;
|
|
}
|
|
}
|
|
return tmid;
|
|
}
|
|
|
|
vec3 seaTransform( in vec3 x ) {
|
|
x.yz = rotate( 0.8, x.yz );
|
|
return x;
|
|
}
|
|
|
|
vec3 seaUntransform( in vec3 x ) {
|
|
x.yz = rotate( -0.8, x.yz );
|
|
return x;
|
|
}
|
|
|
|
void renderSea( const in vec3 ro, const in vec3 rd, inout vec3 n, inout float att ) {
|
|
vec3 p,
|
|
rom = seaTransform(ro),
|
|
rdm = seaTransform(rd);
|
|
|
|
rom.y -= EARTH_RADIUS;
|
|
rom *= 1000.;
|
|
rom.xz += vec2(3.1,.2)*time;
|
|
|
|
SEA_TIME = time * SEA_SPEED;
|
|
|
|
seaHeightMapTracing(rom,rdm,p);
|
|
float squareddist = dot(p - rom, p-rom );
|
|
n = seaGetNormal(p, squareddist * SEA_EPSILON_NRM );
|
|
|
|
n = seaUntransform(n);
|
|
|
|
att = clamp(SEA_HEIGHT+p.y, 0.,1.);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Terrain based on Elevated and Terrain Tubes by IQ
|
|
//
|
|
// https://www.shadertoy.com/view/MdX3Rr
|
|
// https://www.shadertoy.com/view/4sjXzG
|
|
//-----------------------------------------------------
|
|
|
|
#ifndef HIDE_TERRAIN
|
|
|
|
const mat2 terrainM2 = mat2(1.6,-1.2,1.2,1.6);
|
|
|
|
float terrainLow( vec2 p ) {
|
|
p *= 0.0013;
|
|
|
|
float s = 1.0;
|
|
float t = 0.0;
|
|
for( int i=0; i<2; i++ ) {
|
|
t += s*tri( p );
|
|
s *= 0.5 + 0.1*t;
|
|
p = 0.97*terrainM2*p + (t-0.5)*0.12;
|
|
}
|
|
return t*33.0;
|
|
}
|
|
|
|
float terrainMed( vec2 p ) {
|
|
p *= 0.0013;
|
|
|
|
float s = 1.0;
|
|
float t = 0.0;
|
|
for( int i=0; i<6; i++ ) {
|
|
t += s*tri( p );
|
|
s *= 0.5 + 0.1*t;
|
|
p = 0.97*terrainM2*p + (t-0.5)*0.12;
|
|
}
|
|
|
|
return t*33.0;
|
|
}
|
|
|
|
float terrainHigh( vec2 p ) {
|
|
vec2 q = p;
|
|
p *= 0.0013;
|
|
|
|
float s = 1.0;
|
|
float t = 0.0;
|
|
for( int i=0; i<7; i++ ) {
|
|
t += s*tri( p );
|
|
s *= 0.5 + 0.1*t;
|
|
p = 0.97*terrainM2*p + (t-0.5)*0.12;
|
|
}
|
|
|
|
t += t*0.015*fbm( q );
|
|
return t*33.0;
|
|
}
|
|
|
|
float terrainMap( const in vec3 pos ) {
|
|
return pos.y - terrainMed(pos.xz);
|
|
}
|
|
|
|
float terrainMapH( const in vec3 pos ) {
|
|
float y = terrainHigh(pos.xz);
|
|
float h = pos.y - y;
|
|
return h;
|
|
}
|
|
|
|
float terrainIntersect( in vec3 ro, in vec3 rd, in float tmin, in float tmax ) {
|
|
float t = tmin;
|
|
for( int i=0; i<TERRAIN_NUM_STEPS; i++ ) {
|
|
vec3 pos = ro + t*rd;
|
|
float res = terrainMap( pos );
|
|
if( res<(0.001*t) || t>tmax ) break;
|
|
t += res*.9;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
float terrainCalcShadow(in vec3 ro, in vec3 rd ) {
|
|
vec2 eps = vec2(150.0,0.0);
|
|
float h1 = terrainMed( ro.xz );
|
|
float h2 = terrainLow( ro.xz );
|
|
|
|
float d1 = 10.0;
|
|
float d2 = 80.0;
|
|
float d3 = 200.0;
|
|
float s1 = clamp( 1.0*(h1 + rd.y*d1 - terrainMed(ro.xz + d1*rd.xz)), 0.0, 1.0 );
|
|
float s2 = clamp( 0.5*(h1 + rd.y*d2 - terrainMed(ro.xz + d2*rd.xz)), 0.0, 1.0 );
|
|
float s3 = clamp( 0.2*(h2 + rd.y*d3 - terrainLow(ro.xz + d3*rd.xz)), 0.0, 1.0 );
|
|
|
|
return min(min(s1,s2),s3);
|
|
}
|
|
vec3 terrainCalcNormalHigh( in vec3 pos, float t ) {
|
|
vec2 e = vec2(1.0,-1.0)*0.001*t;
|
|
|
|
return normalize( e.xyy*terrainMapH( pos + e.xyy ) +
|
|
e.yyx*terrainMapH( pos + e.yyx ) +
|
|
e.yxy*terrainMapH( pos + e.yxy ) +
|
|
e.xxx*terrainMapH( pos + e.xxx ) );
|
|
}
|
|
|
|
vec3 terrainCalcNormalMed( in vec3 pos, float t ) {
|
|
float e = 0.005*t;
|
|
vec2 eps = vec2(e,0.0);
|
|
float h = terrainMed( pos.xz );
|
|
return normalize(vec3( terrainMed(pos.xz-eps.xy)-h, e, terrainMed(pos.xz-eps.yx)-h ));
|
|
}
|
|
|
|
vec3 terrainTransform( in vec3 x ) {
|
|
x.zy = rotate( -.83, x.zy );
|
|
return x;
|
|
}
|
|
|
|
vec3 terrainUntransform( in vec3 x ) {
|
|
x.zy = rotate( .83, x.zy );
|
|
return x;
|
|
}
|
|
|
|
|
|
float llamelTime;
|
|
const float llamelScale = 5.;
|
|
|
|
vec3 llamelPosition() {
|
|
llamelTime = time*2.5;
|
|
vec2 pos = vec2( -400., 135.-llamelTime*0.075* llamelScale);
|
|
return vec3( pos.x, terrainMed( pos ), pos.y );
|
|
}
|
|
|
|
vec3 terrainShade( const in vec3 col, const in vec3 pos, const in vec3 rd, const in vec3 n, const in float spec,
|
|
const in vec3 sunc, const in vec3 upc, const in vec3 reflc ) {
|
|
vec3 sunDirection = terrainTransform(SUN_DIRECTION);
|
|
float dif = diffuse( n, sunDirection );
|
|
float bac = diffuse( n, vec3(-sunDirection.x, sunDirection.y, -sunDirection.z) );
|
|
float sha = terrainCalcShadow( pos, sunDirection );
|
|
float amb = clamp( n.y,0.0,1.0);
|
|
|
|
vec3 lin = vec3(0.0);
|
|
lin += 2.*dif*sunc*vec3( sha, sha*sha*0.1+0.9*sha, sha*sha*0.2+0.8*sha );
|
|
lin += 0.2*amb*upc;
|
|
lin += 0.08*bac*clamp(vec3(1.)-sunc, vec3(0.), vec3(1.));
|
|
return mix( col*lin*3., reflc, spec*fresnel(n,-terrainTransform(rd),5.0) );
|
|
}
|
|
|
|
vec3 terrainGetColor( const in vec3 pos, const in vec3 rd, const in float t, const in vec3 sunc, const in vec3 upc, const in vec3 reflc ) {
|
|
vec3 nor = terrainCalcNormalHigh( pos, t );
|
|
vec3 sor = terrainCalcNormalMed( pos, t );
|
|
|
|
float spec = 0.005;
|
|
|
|
#ifdef DISPLAY_TERRAIN_DETAIL
|
|
float no = noise(5.*fbm(1.11*pos.xz));
|
|
#else
|
|
const float no = 0.;
|
|
#endif
|
|
float r = .5+.5*fbm(.95*pos.xz);
|
|
vec3 col = (r*0.25+0.75)*0.9*mix( vec3(0.08,0.07,0.07), vec3(0.10,0.09,0.08), noise(0.4267*vec2(pos.x*2.,pos.y*9.8))+.01*no );
|
|
col = mix( col, 0.20*vec3(0.45,.30,0.15)*(0.50+0.50*r),smoothstep(0.825,0.925,nor.y+.025*no) );
|
|
col = mix( col, 0.15*vec3(0.30,.30,0.10)*(0.25+0.75*r),smoothstep(0.95,1.0,nor.y+.025*no) );
|
|
col *= .88+.12*no;
|
|
|
|
float s = nor.y + 0.03*pos.y + 0.35*fbm(0.05*pos.xz) - .35;
|
|
float sf = fwidth(s) * 1.5;
|
|
s = smoothstep(0.84-sf, 0.84+sf, s );
|
|
col = mix( col, 0.29*vec3(0.62,0.65,0.7), s);
|
|
nor = mix( nor, sor, 0.7*smoothstep(0.9, 0.95, s ) );
|
|
spec = mix( spec, 0.45, smoothstep(0.9, 0.95, s ) );
|
|
|
|
col = terrainShade( col, pos, rd, nor, spec, sunc, upc, reflc );
|
|
|
|
#ifdef DISPLAY_LLAMEL
|
|
col *= clamp( distance(pos.xz, llamelPosition().xz )*0.4, 0.4, 1.);
|
|
#endif
|
|
|
|
return col;
|
|
}
|
|
|
|
vec3 terrainTransformRo( const in vec3 ro ) {
|
|
vec3 rom = terrainTransform(ro);
|
|
rom.y -= EARTH_RADIUS - 100.;
|
|
rom.xz *= 5.;
|
|
rom.xz += vec2(-170.,50.)+vec2(-4.,.4)*time;
|
|
rom.y += (terrainLow( rom.xz ) - 86.)*clamp( 1.-1.*(length(ro)-EARTH_RADIUS), 0., 1.);
|
|
return rom;
|
|
}
|
|
|
|
vec4 renderTerrain( const in vec3 ro, const in vec3 rd, inout vec3 intersection, inout vec3 n ) {
|
|
vec3 p,
|
|
rom = terrainTransformRo(ro),
|
|
rdm = terrainTransform(rd);
|
|
|
|
float tmin = 10.0;
|
|
float tmax = 3200.0;
|
|
|
|
float res = terrainIntersect( rom, rdm, tmin, tmax );
|
|
|
|
if( res > tmax ) {
|
|
res = -1.;
|
|
} else {
|
|
vec3 pos = rom+rdm*res;
|
|
n = terrainCalcNormalMed( pos, res );
|
|
n = terrainUntransform( n );
|
|
|
|
intersection = ro+rd*res/100.;
|
|
}
|
|
return vec4(res, rom+rdm*res);
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------
|
|
// LLamels by Eiffie
|
|
//
|
|
// https://www.shadertoy.com/view/ltsGz4
|
|
//-----------------------------------------------------
|
|
#ifdef DISPLAY_LLAMEL
|
|
float llamelMapSMin(const in float a,const in float b,const in float k){
|
|
float h=clamp(0.5+0.5*(b-a)/k,0.0,1.0);return b+h*(a-b-k+k*h);
|
|
}
|
|
|
|
float llamelMapLeg(vec3 p, vec3 j0, vec3 j3, vec3 l, vec4 r, vec3 rt){//z joint with tapered legs
|
|
float lx2z=l.x/(l.x+l.z),h=l.y*lx2z;
|
|
vec3 u=(j3-j0)*lx2z,q=u*(0.5+0.5*(l.x*l.x-h*h)/dot(u,u));
|
|
q+=sqrt(max(0.0,l.x*l.x-dot(q,q)))*normalize(cross(u,rt));
|
|
vec3 j1=j0+q,j2=j3-q*(1.0-lx2z)/lx2z;
|
|
u=p-j0;q=j1-j0;
|
|
h=clamp(dot(u,q)/dot(q,q),0.0,1.0);
|
|
float d=length(u-q*h)-r.x-(r.y-r.x)*h;
|
|
u=p-j1;q=j2-j1;
|
|
h=clamp(dot(u,q)/dot(q,q),0.0,1.0);
|
|
d=min(d,length(u-q*h)-r.y-(r.z-r.y)*h);
|
|
u=p-j2;q=j3-j2;
|
|
h=clamp(dot(u,q)/dot(q,q),0.0,1.0);
|
|
return min(d,length(u-q*h)-r.z-(r.w-r.z)*h);
|
|
}
|
|
|
|
float llamelMap(in vec3 p) {
|
|
const vec3 rt=vec3(0.0,0.0,1.0);
|
|
p.y += 0.25*llamelScale;
|
|
p.xz -= 0.5*llamelScale;
|
|
p.xz = vec2(-p.z, p.x);
|
|
vec3 pori = p;
|
|
|
|
p /= llamelScale;
|
|
|
|
vec2 c=floor(p.xz);
|
|
p.xz=fract(p.xz)-vec2(0.5);
|
|
p.y -= p.x*.04*llamelScale;
|
|
float sa=sin(c.x*2.0+c.y*4.5+llamelTime*0.05)*0.15;
|
|
|
|
float b=0.83-abs(p.z);
|
|
float a=c.x+117.0*c.y+sign(p.x)*1.57+sign(p.z)*1.57+llamelTime,ca=cos(a);
|
|
vec3 j0=vec3(sign(p.x)*0.125,ca*0.01,sign(p.z)*0.05),j3=vec3(j0.x+sin(a)*0.1,max(-0.25+ca*0.1,-0.25),j0.z);
|
|
float dL=llamelMapLeg(p,j0,j3,vec3(0.08,0.075,0.12),vec4(0.03,0.02,0.015,0.01),rt*sign(p.x));
|
|
p.y-=0.03;
|
|
float dB=(length(p.xyz*vec3(1.0,1.75,1.75))-0.14)*0.75;
|
|
a=c.x+117.0*c.y+llamelTime;ca=cos(a);sa*=0.4;
|
|
j0=vec3(0.125,0.03+abs(ca)*0.03,ca*0.01),j3=vec3(0.3,0.07+ca*sa,sa);
|
|
float dH=llamelMapLeg(p,j0,j3,vec3(0.075,0.075,0.06),vec4(0.03,0.035,0.03,0.01),rt);
|
|
dB=llamelMapSMin(min(dL,dH),dB,clamp(0.04+p.y,0.0,1.0));
|
|
a=max(abs(p.z),p.y)+0.05;
|
|
return max(min(dB,min(a,b)),length(pori.xz-vec2(0.5)*llamelScale)-.5*llamelScale);
|
|
}
|
|
|
|
vec3 llamelGetNormal( in vec3 ro ) {
|
|
vec2 e = vec2(1.0,-1.0)*0.001;
|
|
|
|
return normalize( e.xyy*llamelMap( ro + e.xyy ) +
|
|
e.yyx*llamelMap( ro + e.yyx ) +
|
|
e.yxy*llamelMap( ro + e.yxy ) +
|
|
e.xxx*llamelMap( ro + e.xxx ) );
|
|
}
|
|
|
|
vec4 renderLlamel( in vec3 ro, const in vec3 rd, const in vec3 sunc, const in vec3 upc, const in vec3 reflc ) {
|
|
ro -= llamelPosition();
|
|
float t=.1*hash(rd.xy),d,dm=10.0,tm;
|
|
for(int i=0;i<36;i++){
|
|
t+=d=llamelMap(ro+rd*t);
|
|
if(d<dm){dm=d;tm=t;}
|
|
if(t>1000.0 || d<0.00001)break;
|
|
}
|
|
dm=max(0.0,dm);
|
|
if( dm < .02 ) {
|
|
vec3 col = vec3(0.45,.30,0.15)*.2;
|
|
vec3 pos = ro + rd*tm;
|
|
vec3 nor = llamelGetNormal( pos );
|
|
col = terrainShade( col, pos, rd, nor, .01, sunc, upc, reflc );
|
|
return vec4(col, clamp( 1.-(dm-0.01)/0.01,0., 1.) );
|
|
}
|
|
|
|
return vec4(0.);
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------
|
|
// Clouds (by me ;))
|
|
//-----------------------------------------------------
|
|
|
|
vec4 renderClouds( const in vec3 ro, const in vec3 rd, const in float d, const in vec3 n, const in float land,
|
|
const in vec3 sunColor, const in vec3 upColor, inout float shadow ) {
|
|
vec3 intersection = ro+rd*d;
|
|
vec3 cint = intersection*0.009;
|
|
float rot = -.2*length(cint.xy) + .6*fbm( cint*.4,0.5,2.96 ) + .05*land;
|
|
|
|
cint.xy = rotate( rot, cint.xy );
|
|
|
|
vec3 cdetail = mod(intersection*3.23,vec3(50.));
|
|
cdetail.xy = rotate( .25*rot, cdetail.xy );
|
|
|
|
float clouds = 1.3*(fbm( cint*(1.+.02*noise(intersection)),0.5,2.96)+.4*land-.3);
|
|
|
|
#ifdef DISPLAY_CLOUDS_DETAIL
|
|
if( d < 200. ) {
|
|
clouds += .3*(fbm(cdetail,0.5,2.96)-.5)*(1.-smoothstep(0.,200.,d));
|
|
}
|
|
#endif
|
|
|
|
shadow = clamp(1.-clouds, 0., 1.);
|
|
|
|
clouds = clamp(clouds, 0., 1.);
|
|
clouds *= clouds;
|
|
clouds *= smoothstep(0.,0.4,d);
|
|
|
|
vec3 clbasecolor = vec3(1.);
|
|
vec3 clcol = .1*clbasecolor*sunColor * vec3(specular(n,SUN_DIRECTION,rd,36.0));
|
|
clcol += .3*clbasecolor*sunColor;
|
|
clcol += clbasecolor*(diffuse(n,SUN_DIRECTION)*sunColor+upColor);
|
|
|
|
return vec4( clcol, clouds );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Planet (by me ;))
|
|
//-----------------------------------------------------
|
|
|
|
vec4 renderPlanet( const in vec3 ro, const in vec3 rd, const in vec3 up, inout float maxd ) {
|
|
float d = iSphere( ro, rd, vec4( 0., 0., 0., EARTH_RADIUS ) );
|
|
|
|
vec3 intersection = ro + rd*d;
|
|
vec3 n = nSphere( intersection, vec4( 0., 0., 0., EARTH_RADIUS ) );
|
|
vec4 res;
|
|
|
|
#ifndef HIDE_TERRAIN
|
|
bool renderTerrainDetail = length(ro) < EARTH_RADIUS+EARTH_ATMOSPHERE &&
|
|
dot( terrainUntransform( vec3(0.,1.,0.) ), normalize(ro) ) > .9996;
|
|
#endif
|
|
bool renderSeaDetail = d < 1. && dot( seaUntransform( vec3(0.,1.,0.) ), normalize(ro) ) > .9999;
|
|
float mixDetailColor = 0.;
|
|
|
|
if( d < 0. || d > maxd) {
|
|
#ifndef HIDE_TERRAIN
|
|
if( renderTerrainDetail ) {
|
|
intersection = ro;
|
|
n = normalize( ro );
|
|
} else {
|
|
return vec4(0);
|
|
}
|
|
#else
|
|
return vec4(0.);
|
|
#endif
|
|
}
|
|
if( d > 0. ) {
|
|
maxd = d;
|
|
}
|
|
float att = 0.;
|
|
|
|
if( dot(n,SUN_DIRECTION) < -0.1 ) return vec4( 0., 0., 0., 1. );
|
|
|
|
float dm = MAX, e = 0.;
|
|
vec3 col, detailCol, nDetail;
|
|
|
|
// normal and intersection
|
|
#ifndef HIDE_TERRAIN
|
|
if( renderTerrainDetail ) {
|
|
res = renderTerrain( ro, rd, intersection, nDetail );
|
|
if( res.x < 0. && d < 0. ) {
|
|
return vec4(0);
|
|
}
|
|
if( res.x >= 0. ) {
|
|
maxd = pow(res.x/4000.,4.)*50.;
|
|
e = -10.;
|
|
}
|
|
mixDetailColor = 1.-smoothstep(.75, 1., (length(ro)-EARTH_RADIUS) / EARTH_ATMOSPHERE);
|
|
n = normalize( mix( n, nDetail, mixDetailColor ) );
|
|
} else
|
|
#endif
|
|
if( renderSeaDetail ) {
|
|
float attsea, mf = smoothstep(.5,1.,d);
|
|
|
|
renderSea( ro, rd, nDetail, attsea );
|
|
|
|
n = normalize(mix( nDetail, n, mf ));
|
|
att = mix( attsea, att, mf );
|
|
} else {
|
|
e = fbm( .003*intersection+vec3(1.),0.4,2.96) + smoothstep(.85,.95, abs(intersection.z/EARTH_RADIUS));
|
|
#ifndef HIDE_TERRAIN
|
|
if( d < 1500. ) {
|
|
e += (-.03+.06* fbm( intersection*0.1,0.4,2.96))*(1.-d/1500.);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
vec3 sunColor = .25*renderAtmosphericLow( intersection, SUN_DIRECTION).xyz;
|
|
vec3 upColor = 2.*renderAtmosphericLow( intersection, n).xyz;
|
|
vec3 reflColor = renderAtmosphericLow( intersection, reflect(rd,n)).xyz;
|
|
|
|
// color
|
|
#ifndef HIDE_TERRAIN
|
|
if(renderTerrainDetail ) {
|
|
detailCol = col = terrainGetColor(res.yzw, rd, res.x, sunColor, upColor, reflColor);
|
|
d = 0.;
|
|
}
|
|
#endif
|
|
|
|
if( mixDetailColor < 1. ) {
|
|
if( e < .45 ) {
|
|
// sea
|
|
col = seaGetColor(n,rd,SUN_DIRECTION, att, sunColor, upColor, reflColor);
|
|
} else {
|
|
// planet (land) far
|
|
float land1 = max(0.1, fbm( intersection*0.0013,0.4,2.96) );
|
|
float land2 = max(0.1, fbm( intersection*0.0063,0.4,2.96) );
|
|
float iceFactor = abs(pow(intersection.z/EARTH_RADIUS,13.0))*e;
|
|
|
|
vec3 landColor1 = vec3(0.43,0.65,0.1) * land1;
|
|
vec3 landColor2 = RING_COLOR_1 * land2;
|
|
vec3 mixedLand = (landColor1 + landColor2)* 0.5;
|
|
vec3 finalLand = mix(mixedLand, vec3(7.0, 7.0, 7.0) * land1 * 1.5, max(iceFactor+.02*land2-.02, 0.));
|
|
|
|
col = (diffuse(n,SUN_DIRECTION)*sunColor+upColor)*finalLand*.75;
|
|
#ifdef HIGH_QUALITY
|
|
col *= (.5+.5*fbm( intersection*0.23,0.4,2.96) );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if( mixDetailColor > 0. ) {
|
|
col = mix( col, detailCol, mixDetailColor );
|
|
}
|
|
|
|
#ifdef DISPLAY_LLAMEL
|
|
if(renderTerrainDetail ) {
|
|
vec3 rom = terrainTransformRo(ro),
|
|
rdm = terrainTransform(rd);
|
|
d = iSphere( rom, rdm, vec4( llamelPosition(), llamelScale*3. ) );
|
|
if( d > 0. ) {
|
|
vec4 llamel = renderLlamel( rom+rdm*d, rdm, sunColor, upColor, reflColor );
|
|
col = mix(col, llamel.rgb, llamel.a);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
d = iSphere( ro, rd, vec4( 0., 0., 0., EARTH_RADIUS+EARTH_CLOUDS ) );
|
|
if( d > 0. ) {
|
|
float shadow;
|
|
vec4 clouds = renderClouds( ro, rd, d, n, e, sunColor, upColor, shadow);
|
|
col *= shadow;
|
|
col = mix( col, clouds.rgb, clouds.w );
|
|
}
|
|
|
|
float m = MAX;
|
|
col *= (1. - renderRingFarShadow( ro+rd*d, SUN_DIRECTION ) );
|
|
|
|
return vec4( col, 1. );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// Lens flare by musk
|
|
//
|
|
// https://www.shadertoy.com/view/4sX3Rs
|
|
//-----------------------------------------------------
|
|
|
|
vec3 lensFlare( const in vec2 uv, const in vec2 pos) {
|
|
vec2 main = uv-pos;
|
|
vec2 uvd = uv*(length(uv));
|
|
|
|
float f0 = 1.5/(length(uv-pos)*16.0+1.0);
|
|
|
|
float f1 = max(0.01-pow(length(uv+1.2*pos),1.9),.0)*7.0;
|
|
|
|
float f2 = max(1.0/(1.0+32.0*pow(length(uvd+0.8*pos),2.0)),.0)*00.25;
|
|
float f22 = max(1.0/(1.0+32.0*pow(length(uvd+0.85*pos),2.0)),.0)*00.23;
|
|
float f23 = max(1.0/(1.0+32.0*pow(length(uvd+0.9*pos),2.0)),.0)*00.21;
|
|
|
|
vec2 uvx = mix(uv,uvd,-0.5);
|
|
|
|
float f4 = max(0.01-pow(length(uvx+0.4*pos),2.4),.0)*6.0;
|
|
float f42 = max(0.01-pow(length(uvx+0.45*pos),2.4),.0)*5.0;
|
|
float f43 = max(0.01-pow(length(uvx+0.5*pos),2.4),.0)*3.0;
|
|
|
|
vec3 c = vec3(.0);
|
|
|
|
c.r+=f2+f4; c.g+=f22+f42; c.b+=f23+f43;
|
|
c = c*.5 - vec3(length(uvd)*.05);
|
|
c+=vec3(f0);
|
|
|
|
return c;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// cameraPath
|
|
//-----------------------------------------------------
|
|
|
|
vec3 pro, pta, pup;
|
|
float dro, dta, dup;
|
|
|
|
void camint( inout vec3 ret, const in float t, const in float duration, const in vec3 dest, inout vec3 prev, inout float prevt ) {
|
|
if( t >= prevt && t <= prevt+duration ) {
|
|
ret = mix( prev, dest, smoothstep(prevt, prevt+duration, t) );
|
|
}
|
|
prev = dest;
|
|
prevt += duration;
|
|
}
|
|
|
|
void cameraPath( in float t, out vec3 ro, out vec3 ta, out vec3 up ) {
|
|
#ifndef HIDE_TERRAIN
|
|
time = t = mod( t, 92. );
|
|
#else
|
|
time = t = mod( t, 66. );
|
|
#endif
|
|
dro = dta = dup = 0.;
|
|
|
|
pro = ro = vec3(900. ,7000. ,1500. );
|
|
pta = ta = vec3( 0. , 0. , 0. );
|
|
pup = up = vec3( 0. , 0.4, 1. );
|
|
|
|
camint( ro, t, 5., vec3(-4300. ,-1000. , 500. ), pro, dro );
|
|
camint( ta, t, 5., vec3( 0. , 0. , 0. ), pta, dta );
|
|
camint( up, t, 7., vec3( 0. , 0.1, 1. ), pup, dup );
|
|
|
|
camint( ro, t, 3., vec3(-1355. , 1795. , 1.2 ), pro, dro );
|
|
camint( ta, t, 1., vec3( 0. , 300. ,-600. ), pta, dta );
|
|
camint( up, t, 6., vec3( 0. , 0.1, 1. ), pup, dup );
|
|
|
|
camint( ro, t, 10., vec3(-1355. , 1795. , 1.2 ), pro, dro );
|
|
camint( ta, t, 14., vec3( 0. , 100. , 600. ), pta, dta );
|
|
camint( up, t, 13., vec3( 0. , 0.3, 1. ), pup, dup );
|
|
|
|
vec3 roe = seaUntransform( vec3( 0., EARTH_RADIUS+0.004, 0. ) );
|
|
vec3 upe = seaUntransform( vec3( 0., 1., 0. ) );
|
|
|
|
camint( ro, t, 7.,roe, pro, dro );
|
|
camint( ta, t, 7., vec3( EARTH_RADIUS + 0., EARTH_RADIUS - 500., 500. ), pta, dta );
|
|
camint( up, t, 6., upe, pup, dup );
|
|
|
|
camint( ro, t, 17.,roe, pro, dro );
|
|
camint( ta, t, 17., vec3( EARTH_RADIUS + 500., EARTH_RADIUS + 1300., -100. ), pta, dta );
|
|
camint( up, t, 18., vec3(.0,1.,1.), pup, dup );
|
|
|
|
camint( ro, t, 11., vec3( 3102. , 0. , 1450. ), pro, dro );
|
|
camint( ta, t, 4., vec3( 0. , -100. , 0. ), pta, dta );
|
|
camint( up, t, 8., vec3( 0. , 0.15, 1. ), pup, dup );
|
|
#ifndef HIDE_TERRAIN
|
|
roe = terrainUntransform( vec3( 0., EARTH_RADIUS+0.004, 0. ) );
|
|
upe = terrainUntransform( vec3( 0., 1., 0. ) );
|
|
|
|
camint( ro, t, 7., roe, pro, dro );
|
|
camint( ta, t, 12., vec3( -EARTH_RADIUS, EARTH_RADIUS+200., 100.), pta, dta );
|
|
camint( up, t, 2., upe, pup, dup );
|
|
|
|
roe = terrainUntransform( vec3( 0., EARTH_RADIUS+0.001, 0. ) );
|
|
camint( ro, t, 17.,roe, pro, dro );
|
|
camint( ta, t, 18., roe + vec3( 5000., EARTH_RADIUS-100., -2000.), pta, dta );
|
|
camint( up, t, 18., vec3(.0,1.,1.), pup, dup );
|
|
|
|
roe = terrainUntransform( vec3( 0., EARTH_RADIUS+1.8, 0. ) );
|
|
camint( ro, t, 4.,roe, pro, dro );
|
|
camint( ta, t, 4.5, roe + vec3( EARTH_RADIUS, EARTH_RADIUS+2000., -30.), pta, dta );
|
|
camint( up, t, 4., vec3(.0,1.,1.), pup, dup );
|
|
#endif
|
|
camint( ro, t, 10., vec3(900. ,7000. , 1500. ), pro, dro );
|
|
camint( ta, t, 2., vec3( 0. , 0. , 0. ), pta, dta );
|
|
camint( up, t, 10., vec3( 0. , 0.4, 1. ), pup, dup );
|
|
|
|
up = normalize( up );
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// mainImage
|
|
//-----------------------------------------------------
|
|
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
|
|
vec2 uv = fragCoord.xy / iResolution.xy;
|
|
|
|
vec2 p = -1.0 + 2.0 * (fragCoord.xy) / iResolution.xy;
|
|
p.x *= iResolution.x/iResolution.y;
|
|
|
|
vec3 col;
|
|
|
|
// black bands
|
|
vec2 bandy = vec2(.1,.9);
|
|
if( uv.y < bandy.x || uv.y > bandy.y ) {
|
|
col = vec3(0.);
|
|
} else {
|
|
// camera
|
|
vec3 ro, ta, up;
|
|
cameraPath( iGlobalTime*.7, ro, ta, up );
|
|
|
|
vec3 ww = normalize( ta - ro );
|
|
vec3 uu = normalize( cross(ww,up) );
|
|
vec3 vv = normalize( cross(uu,ww));
|
|
vec3 rd = normalize( -p.x*uu + p.y*vv + 2.2*ww );
|
|
|
|
float maxd = MAX;
|
|
col = renderStars( rd ).xyz;
|
|
|
|
vec4 planet = renderPlanet( ro, rd, up, maxd );
|
|
if( planet.w > 0. ) col.xyz = planet.xyz;
|
|
|
|
float atmosphered = maxd;
|
|
vec4 atmosphere = .85*renderAtmospheric( ro, rd, atmosphered );
|
|
col = col * (1.-atmosphere.w ) + atmosphere.xyz;
|
|
|
|
vec4 ring = renderRing( ro, rd, maxd );
|
|
if( ring.w > 0. && atmosphered < maxd ) {
|
|
ring.xyz = ring.xyz * (1.-atmosphere.w ) + atmosphere.xyz;
|
|
}
|
|
col = col * (1.-ring.w ) + ring.xyz;
|
|
|
|
#ifdef DISPLAY_CLOUDS
|
|
float lro = length(ro);
|
|
if( lro < EARTH_RADIUS+EARTH_CLOUDS*1.25 ) {
|
|
vec3 sunColor = 2.*renderAtmosphericLow( ro, SUN_DIRECTION);
|
|
vec3 upColor = 4.*renderAtmosphericLow( ro, vec3(-SUN_DIRECTION.x, SUN_DIRECTION.y, -SUN_DIRECTION.z));
|
|
|
|
if( lro < EARTH_RADIUS+EARTH_CLOUDS ) {
|
|
// clouds
|
|
float d = iCSphereF( ro, rd, EARTH_RADIUS + EARTH_CLOUDS );
|
|
if( d < maxd ) {
|
|
float shadow;
|
|
vec4 clouds = renderClouds( ro, rd, d, normalize(ro), 0., sunColor, upColor, shadow );
|
|
clouds.w *= 1.-smoothstep(0.8*EARTH_CLOUDS,EARTH_CLOUDS,lro-EARTH_RADIUS);
|
|
col = mix(col, clouds.rgb, clouds.w * (1.-smoothstep( 10., 30., d)) );
|
|
}
|
|
}
|
|
float offset = lro-EARTH_RADIUS-EARTH_CLOUDS;
|
|
col = mix( col, .5*sunColor, .15*abs(noise(offset*100.))*clamp(1.-4.*abs(offset)/EARTH_CLOUDS, 0., 1.) );
|
|
}
|
|
#endif
|
|
|
|
// post processing
|
|
col = pow( clamp(col,0.0,1.0), vec3(0.4545) );
|
|
col *= vec3(1.,0.99,0.95);
|
|
col = clamp(1.06*col-0.03, 0., 1.);
|
|
|
|
vec2 sunuv = 2.7*vec2( dot( SUN_DIRECTION, -uu ), dot( SUN_DIRECTION, vv ) );
|
|
float flare = dot( SUN_DIRECTION, normalize(ta-ro) );
|
|
col += vec3(1.4,1.2,1.0)*lensFlare(p, sunuv)*clamp( flare+.3, 0., 1.);
|
|
|
|
uv.y = (uv.y-bandy.x)*(1./(bandy.y-bandy.x));
|
|
col *= 0.5 + 0.5*pow( 16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y), 0.1 );
|
|
}
|
|
fragColor = vec4( col ,1.0);
|
|
}
|