// Luminescence by Martijn Steinrucken aka BigWings - 2017 // countfrolic@gmail.com // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. // My entry for the monthly challenge (May 2017) on r/proceduralgeneration // Use the mouse to look around. Uncomment the SINGLE define to see one specimen by itself. // Code is a bit of a mess, too lazy to clean up. Hope you like it! // Music by Klaus Lunde // https://soundcloud.com/klauslunde/zebra-tribute // YouTube: The Art of Code -> https://www.youtube.com/channel/UCcAlTqd9zID6aNX3TzwxJXg // Twitter: @Steinrucken #define INVERTMOUSE -1. #define MAX_STEPS 100. #define VOLUME_STEPS 8. //#define SINGLE #define MIN_DISTANCE 0.1 #define MAX_DISTANCE 100. #define HIT_DISTANCE .01 #define S(x,y,z) smoothstep(x,y,z) #define B(x,y,z,w) S(x-z, x+z, w)*S(y+z, y-z, w) #define sat(x) clamp(x,0.,1.) #define SIN(x) sin(x)*.5+.5 const vec3 lf=vec3(1., 0., 0.); const vec3 up=vec3(0., 1., 0.); const vec3 fw=vec3(0., 0., 1.); const float halfpi = 1.570796326794896619; const float pi = 3.141592653589793238; const float twopi = 6.283185307179586; vec3 accentColor1 = vec3(1., .1, .5); vec3 secondColor1 = vec3(.1, .5, 1.); vec3 accentColor2 = vec3(1., .5, .1); vec3 secondColor2 = vec3(.1, .5, .6); vec3 bg; // global background color vec3 accent; // color of the phosphorecence float N1( float x ) { return fract(sin(x)*5346.1764); } float N2(float x, float y) { return N1(x + y*23414.324); } float N3(vec3 p) { p = fract( p*0.3183099+.1 ); p *= 17.0; return fract( p.x*p.y*p.z*(p.x+p.y+p.z) ); } struct ray { vec3 o; vec3 d; }; struct camera { vec3 p; // the position of the camera vec3 forward; // the camera forward vector vec3 left; // the camera left vector vec3 up; // the camera up vector vec3 center; // the center of the screen, in world coords vec3 i; // where the current ray intersects the screen, in world coords ray ray; // the current ray: from cam pos, through current uv projected on screen vec3 lookAt; // the lookat point float zoom; // the zoom factor }; struct de { // data type used to pass the various bits of information used to shade a de object float d; // final distance to field float m; // material vec3 uv; float pump; vec3 id; vec3 pos; // the world-space coordinate of the fragment }; struct rc { // data type used to handle a repeated coordinate vec3 id; // holds the floor'ed coordinate of each cell. Used to identify the cell. vec3 h; // half of the size of the cell vec3 p; // the repeated coordinate //vec3 c; // the center of the cell, world coordinates }; rc Repeat(vec3 pos, vec3 size) { rc o; o.h = size*.5; o.id = floor(pos/size); // used to give a unique id to each cell o.p = mod(pos, size)-o.h; //o.c = o.id*size+o.h; return o; } camera cam; void CameraSetup(vec2 uv, vec3 position, vec3 lookAt, float zoom) { cam.p = position; cam.lookAt = lookAt; cam.forward = normalize(cam.lookAt-cam.p); cam.left = cross(up, cam.forward); cam.up = cross(cam.forward, cam.left); cam.zoom = zoom; cam.center = cam.p+cam.forward*cam.zoom; cam.i = cam.center+cam.left*uv.x+cam.up*uv.y; cam.ray.o = cam.p; // ray origin = camera position cam.ray.d = normalize(cam.i-cam.p); // ray direction is the vector from the cam pos through the point on the imaginary screen } // ============== Functions I borrowed ;) // 3 out, 1 in... DAVE HOSKINS vec3 N31(float p) { vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787)); p3 += dot(p3, p3.yzx + 19.19); return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); } // DE functions from IQ float smin( float a, float b, float k ) { float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 ); return mix( b, a, h ) - k*h*(1.0-h); } float smax( float a, float b, float k ) { float h = clamp( 0.5 + 0.5*(b-a)/k, 0.0, 1.0 ); return mix( a, b, h ) + k*h*(1.0-h); } float sdSphere( vec3 p, vec3 pos, float s ) { return (length(p-pos)-s); } // From http://mercury.sexy/hg_sdf vec2 pModPolar(inout vec2 p, float repetitions, float fix) { float angle = twopi/repetitions; float a = atan(p.y, p.x) + angle/2.; float r = length(p); float c = floor(a/angle); a = mod(a,angle) - (angle/2.)*fix; p = vec2(cos(a), sin(a))*r; return p; } // ------------------------- float Dist( vec2 P, vec2 P0, vec2 P1 ) { //2d point-line distance vec2 v = P1 - P0; vec2 w = P - P0; float c1 = dot(w, v); float c2 = dot(v, v); if (c1 <= 0. ) // before P0 return length(P-P0); float b = c1 / c2; vec2 Pb = P0 + b*v; return length(P-Pb); } vec3 ClosestPoint(vec3 ro, vec3 rd, vec3 p) { // returns the closest point on ray r to point p return ro + max(0., dot(p-ro, rd))*rd; } vec2 RayRayTs(vec3 ro1, vec3 rd1, vec3 ro2, vec3 rd2) { // returns the two t's for the closest point between two rays // ro+rd*t1 = ro2+rd2*t2 vec3 dO = ro2-ro1; vec3 cD = cross(rd1, rd2); float v = dot(cD, cD); float t1 = dot(cross(dO, rd2), cD)/v; float t2 = dot(cross(dO, rd1), cD)/v; return vec2(t1, t2); } float DistRaySegment(vec3 ro, vec3 rd, vec3 p1, vec3 p2) { // returns the distance from ray r to line segment p1-p2 vec3 rd2 = p2-p1; vec2 t = RayRayTs(ro, rd, p1, rd2); t.x = max(t.x, 0.); t.y = clamp(t.y, 0., length(rd2)); vec3 rp = ro+rd*t.x; vec3 sp = p1+rd2*t.y; return length(rp-sp); } vec2 sph(vec3 ro, vec3 rd, vec3 pos, float radius) { // does a ray sphere intersection // returns a vec2 with distance to both intersections // if both a and b are MAX_DISTANCE then there is no intersection vec3 oc = pos - ro; float l = dot(rd, oc); float det = l*l - dot(oc, oc) + radius*radius; if (det < 0.0) return vec2(MAX_DISTANCE); float d = sqrt(det); float a = l - d; float b = l + d; return vec2(a, b); } vec3 background(vec3 r) { float x = atan(r.x, r.z); // from -pi to pi float y = pi*0.5-acos(r.y); // from -1/2pi to 1/2pi vec3 col = bg*(1.+y); float t = iGlobalTime; // add god rays float a = sin(r.x); float beam = sat(sin(10.*x+a*y*5.+t)); beam *= sat(sin(7.*x+a*y*3.5-t)); float beam2 = sat(sin(42.*x+a*y*21.-t)); beam2 *= sat(sin(34.*x+a*y*17.+t)); beam += beam2; col *= 1.+beam*.05; return col; } float remap(float a, float b, float c, float d, float t) { return ((t-a)/(b-a))*(d-c)+c; } de map( vec3 p, vec3 id ) { float t = iGlobalTime*2.; float N = N3(id); de o; o.m = 0.; float x = (p.y+N*twopi)*1.+t; float r = 1.; float pump = cos(x+cos(x))+sin(2.*x)*.2+sin(4.*x)*.02; x = t + N*twopi; p.y -= (cos(x+cos(x))+sin(2.*x)*.2)*.6; p.xz *= 1. + pump*.2; float d1 = sdSphere(p, vec3(0., 0., 0.), r); float d2 = sdSphere(p, vec3(0., -.5, 0.), r); o.d = smax(d1, -d2, .1); o.m = 1.; if(p.y<.5) { float sway = sin(t+p.y+N*twopi)*S(.5, -3., p.y)*N*.3; p.x += sway*N; // add some sway to the tentacles p.z += sway*(1.-N); vec3 mp = p; mp.xz = pModPolar(mp.xz, 6., 0.); float d3 = length(mp.xz-vec2(.2, .1))-remap(.5, -3.5, .1, .01, mp.y); if(d3MAX_DISTANCE) break; d+=min(s.d, dC); // move to distance to next cell or surface, whichever is closest } if(s.d0.) { vec3 n = calcNormal(o); float lambert = sat(dot(n, L)); vec3 R = reflect(camRay.d, n); float fresnel = sat(1.+dot(camRay.d, n)); float trans = (1.-fresnel)*.5; vec3 ref = background(R); float fade = 0.; if(o.m==1.) { // hood color float density = 0.; for(float i=0.; i.95) { // move cam automatically when mouse is not used m = vec2(t*.25, SIN(t*pi)*.5+.5); } accent = mix(accentColor1, accentColor2, SIN(t*15.456)); bg = mix(secondColor1, secondColor2, SIN(t*7.345231)); float turn = (.1-m.x)*twopi; float s = sin(turn); float c = cos(turn); mat3 rotX = mat3(c, 0., s, 0., 1., 0., s, 0., -c); #ifdef SINGLE float camDist = -10.; #else float camDist = -.1; #endif vec3 lookAt = vec3(0., -1., 0.); vec3 camPos = vec3(0., INVERTMOUSE*camDist*cos((m.y)*pi), camDist)*rotX; CameraSetup(uv, camPos+lookAt, lookAt, 1.); vec3 col = render(uv, cam.ray, 0.); col = pow(col, vec3(mix(1.5, 2.6, SIN(t+pi)))); // post-processing float d = 1.-dot(uv, uv); // vignette col *= (d*d*d)+.1; fragColor = vec4(col, 1.); } vec3 getSkyboxColor() { return mainImage(); }