#version 450 /* Quasi Infinite Zoom Voronoi --------------------------- The infinite zoom effect has been keeping me amused for years. This one is based on something I wrote some time ago, but was inspired by Fabrice Neyret's "Infinite Fall" shader. I've aired on the side of caution and called it "quasi infinite," just in case it doesn't adhere to his strict infinite zoom standards. :) Seriously though, I put together a couple of overly optimized versions a couple of days ago, just for fun, and Fabrice's comments were pretty helpful. I also liked the way he did the layer rotation in his "Infinite Fall" version, so I'm using that. The rest is stock standard infinite zoom stuff that has been around for years. Most people like to use noise for this effect, so I figured I'd do something different and use Voronoi. I've also bump mapped it, added specular highlights, etc. It was tempting to add a heap of other things, but I wanted to keep the example relatively simple. By the way, most of the code is basic bump mapping and lighting. The infinite zoom code takes up just a small portion. Fabrice Neyret's versions: infinite fall - short https://www.shadertoy.com/view/ltjXWW infinite fall - FabriceNeyret2 https://www.shadertoy.com/view/4sl3RX Other examples: Fractal Noise - mu6k https://www.shadertoy.com/view/Msf3Wr Infinite Sierpinski - gleurop https://www.shadertoy.com/view/MdfGR8 Infinite Zoom - fizzer https://www.shadertoy.com/view/MlXGW7 Private link to a textured version of this. Bumped Infinite Zoom Texture - Shane https://www.shadertoy.com/view/Xl2XWw */ 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; /* Fractal Flythrough ------------------ Moving a camera through a fractal object. It's a work in progress. I was looking at one of Dr2's shaders that involved moving a camera through a set of way points (set out on the XZ plane), and thought it'd be cool to do a similar 3D version. The idea was to create a repetitive kind of fractal object, give the open space nodes a set random direction, create some spline points, then run a smooth camera through them. Simple... right? It always seems simple in my head, but gets progressively harder when I try it in a shader. :) I've run into that classic up-vector, camera flipping problem... At least, I think that's the problem? Anyway, I'm hoping the solution is simple, and that someone reading this will be able to point me in the right direction. For now, I've set up a set of 16 random looping points that the camera seems reasonably comfortable with. Just for the record, the general setup works nicely, until the camera loops back on itself in the YZ plane. I'm guessing that increasing the number of way points may eradicate some of the intermittent camera spinning, but I figured I'd leave things alone and treat it as a feature. :) By the way, I was thankful to have Otavio Good's spline setup in his "Alien Beacon" shader as a reference. On a side note, that particular shader is one of my all time favorites on this site. The rendering materials are slightly inspired by the Steampunk genre. Timber, granite, brass, etc. It needs spinning turbines, gears, rivots, and so forth, but that stuff's expensive. Maybe later. Tambako Jaguar did a really cool shader in the Steampunk aesthetic. The link is below. Besides camera path, there's a whole bunch of improvements I'd like to make to this. I've relied on occlusion to mask the fact that there are no shadows. I'm hoping to free up some cycles, so I can put them back in. I'd also like to add extra detail, but that also slows things down. As for the comments, they're very rushed, but I'll tidy those up as well. References: Alien Beacon - Otavio Good https://www.shadertoy.com/view/ld2SzK Steampunk Turbine - TambakoJaguar https://www.shadertoy.com/view/lsd3zf // The main inspiration for this shader. Mandelmaze in Daylight - dr2 https://www.shadertoy.com/view/MdVGRc */ const float FAR = 50.0; // Far plane. // Used to identify individual scene objects. In this case, there are only three: The metal framework, the gold // and the timber. float objID = 0.; // Wood = 1., Metal = 2., Gold = 3.. // Simple hash function. float hash( float n ){ return fract(cos(n)*45758.5453); } // Tri-Planar blending function. Based on an old Nvidia writeup: // GPU Gems 3 - Ryan Geiss: https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch01.html vec3 tex3D(sampler2D t, in vec3 p, in vec3 n ){ n = max(abs(n), 0.001); n /= dot(n, vec3(1)); vec3 tx = texture(t, p.yz).xyz; vec3 ty = texture(t, p.zx).xyz; vec3 tz = texture(t, p.xy).xyz; // Textures are stored in sRGB (I think), so you have to convert them to linear space // (squaring is a rough approximation) prior to working with them... or something like that. :) // Once the final color value is gamma corrected, you should see correct looking colors. return (tx*tx*n.x + ty*ty*n.y + tz*tz*n.z); } // Common formula for rounded squares, for all intended purposes. float lengthN(in vec2 p, in float n){ p = pow(abs(p), vec2(n)); return pow(p.x + p.y, 1.0/n); } // The camera path: There are a few spline setups on Shadertoy, but this one is a slight variation of // Otavio Good's spline setup in his "Alien Beacon" shader: https://www.shadertoy.com/view/ld2SzK // // Spline point markers ("cp" for camera point). The camera visits each point in succession, then loops // back to the first point, when complete, in order to repeat the process. In case it isn't obvious, each // point represents an open space juncture in the object that links to the previous and next point. // Of course, running a camera in a straight line between points wouldn't produce a smooth camera effect, // so we apply the Catmull-Rom equation to the line segment. vec3 cp[16]; void setCamPath(){ // The larger fractal object has nodes in a 4x4x4 grid. // The smaller one in a 2x2x2 grid. The following points // map a path to various open areas throughout the object. const float sl = 2.*.96; const float bl = 4.*.96; cp[0] = vec3(0, 0, 0); cp[1] = vec3(0, 0, bl); cp[2] = vec3(sl, 0, bl); cp[3] = vec3(sl, 0, sl); cp[4] = vec3(sl, sl, sl); cp[5] = vec3(-sl, sl, sl); cp[6] = vec3(-sl, 0, sl); cp[7] = vec3(-sl, 0, 0); cp[8] = vec3(0, 0, 0); cp[9] = vec3(0, 0, -bl); cp[10] = vec3(0, bl, -bl); cp[11] = vec3(-sl, bl, -bl); cp[12] = vec3(-sl, 0, -bl); cp[13] = vec3(-sl, 0, 0); cp[14] = vec3(-sl, -sl, 0); cp[15] = vec3(0, -sl, 0); // Tighening the radius a little, so that the camera doesn't hit the walls. // I should probably hardcode this into the above... Done. //for(int i=0; i<16; i++) cp[i] *= .96; } // Standard Catmull-Rom equation. The equation takes in the line segment end points (p1 and p2), the // points on either side (p0 and p3), the current fractional distance (t) along the segment, then // returns the the smooth (cubic interpolated) position. The end result is a smooth transition // between points... Look up a diagram on the internet. That should make it clearer. vec3 Catmull(vec3 p0, vec3 p1, vec3 p2, vec3 p3, float t){ return (((-p0 + p1*3. - p2*3. + p3)*t*t*t + (p0*2. - p1*5. + p2*4. - p3)*t*t + (-p0 + p2)*t + p1*2.)*.5); } // Camera path. Determine the segment number (segNum), and how far - timewise - we are along it (segTime). // Feed the segment, the appropriate adjoining segments, and the segment time into the Catmull-Rom // equation to produce a camera position. The process is pretty simple, once you get the hang of it. vec3 camPath(float t){ const int aNum = 16; t = fract(t/float(aNum))*float(aNum); // Repeat every 16 time units. // Segment number. Range: [0, 15], in this case. float segNum = floor(t); // Segment portion. Analogous to how far we are alone the individual line segment. Range: [0, 1]. float segTime = t - segNum; if (segNum == 0.) return Catmull(cp[aNum-1], cp[0], cp[1], cp[2], segTime); for(int i=1; iFAR) break; // Alternative: 0.001*max(t*.25, 1.) t += h*.8; } return t; } // The reflections are pretty subtle, so not much effort is being put into them. Only eight iterations. float refTrace(vec3 ro, vec3 rd){ float t = 0.0; for(int i=0; i<16; i++){ float d = map(ro + rd*t); if (d < 0.0025*(t*.25 + 1.) || t>FAR) break; t += d; } return t; } /* // Tetrahedral normal, to save a couple of "map" calls. Courtesy of IQ. vec3 calcNormal(in vec3 p){ // Note the slightly increased sampling distance, to alleviate artifacts due to hit point inaccuracies. vec2 e = vec2(0.0025, -0.0025); return normalize(e.xyy * map(p + e.xyy) + e.yyx * map(p + e.yyx) + e.yxy * map(p + e.yxy) + e.xxx * map(p + e.xxx)); } */ // Standard normal function. It's not as fast as the tetrahedral calculation, but more symmetrical. Due to // the intricacies of this particular scene, it's kind of needed to reduce jagged effects. vec3 calcNormal(in vec3 p) { const vec2 e = vec2(0.005, 0); return normalize(vec3(map(p + e.xyy) - map(p - e.xyy), map(p + e.yxy) - map(p - e.yxy), map(p + e.yyx) - map(p - e.yyx))); } // I keep a collection of occlusion routines... OK, that sounded really nerdy. :) // Anyway, I like this one. I'm assuming it's based on IQ's original. float calcAO(in vec3 pos, in vec3 nor) { float sca = 2.0, occ = 0.0; for( int i=0; i<5; i++ ){ float hr = 0.01 + float(i)*0.5/4.0; float dd = map(nor * hr + pos); occ += (hr - dd)*sca; sca *= 0.7; } return clamp( 1.0 - occ, 0.0, 1.0 ); } // Texture bump mapping. Four tri-planar lookups, or 12 texture lookups in total. I tried to // make it as concise as possible. Whether that translates to speed, or not, I couldn't say. vec3 texBump( sampler2D tx, in vec3 p, in vec3 n, float bf){ const vec2 e = vec2(0.001, 0); // Three gradient vectors rolled into a matrix, constructed with offset greyscale texture values. mat3 m = mat3( tex3D(tx, p - e.xyy, n), tex3D(tx, p - e.yxy, n), tex3D(tx, p - e.yyx, n)); vec3 g = vec3(0.299, 0.587, 0.114)*m; // Converting to greyscale. g = (g - dot(tex3D(tx, p , n), vec3(0.299, 0.587, 0.114)) )/e.x; g -= n*dot(n, g); return normalize( n + g*bf ); // Bumped normal. "bf" - bump factor. } void mainImage( out vec4 fragColor, in vec2 fragCoord ){ // Screen coordinates. vec2 u = (fragCoord - iResolution.xy*0.5)/iResolution.y; float speed = iGlobalTime*0.35 + 8.; // Initiate the camera path spline points. Kind of wasteful not making this global, but I wanted // it self contained... for better or worse. I'm not really sure what the GPU would prefer. setCamPath(); // Camera Setup. vec3 ro = camPath(speed); // Camera position, doubling as the ray origin. vec3 lk = camPath(speed + .5); // "Look At" position. vec3 lp = camPath(speed + .5) + vec3(0, .25, 0); // Light position, somewhere near the moving camera. // Using the above to produce the unit ray-direction vector. float FOV = 1.57; // FOV - Field of view. vec3 fwd = normalize(lk-ro); vec3 rgt = normalize(vec3(fwd.z, 0, -fwd.x)); vec3 up = (cross(fwd, rgt)); // Unit direction ray. vec3 rd = normalize(fwd + FOV*(u.x*rgt + u.y*up)); // Raymarch the scene. float t = trace(ro, rd); // Initialize the scene color. vec3 col = vec3(0); // Scene hit, so color the pixel. Technically, the object should always be hit, so it's tempting to // remove this entire branch... but I'll leave it, for now. if(t1.5 && rSaveObjID<2.5){ rCol = vec3(1)*dot(rCol, vec3(.299, .587, .114))*.7 + rCol*.15;//*.7+.2 //rDiff *= 1.35; } if(rSaveObjID>2.5){ //float rc = dot(rCol, vec3(.299, .587, .114)); vec3 rFire = pow(vec3(1.5, 1, 1)*rCol, vec3(8, 2, 1.5));//*.5+rc*.5; rCol = min(mix(vec3(1.5, .9, .375), vec3(.75, .375, .3), rFire), 2.)*.5 + rCol; } rCol *= (rDiff + .35)*rAtten; // Reflected color. Not accurate, but close enough. // Grey metal inner tubing. if(saveObjID>1.5 && saveObjID<2.5){ // Grey out the limestone wall color. col = vec3(1)*dot(col, vec3(.299, .587, .114))*.7 + col*.15; refl = .5; //dif *= 1.35; //spe2 *= 1.35; } // Gold trimming properties. More effort should probably be put in here. // I could just write "saveObjID == 3.," but I get a little paranoid where floats are concerned. :) if(saveObjID>2.5){ // For the screen image, we're interested in the offset height and depth positions. Ie: pOffs.zy. // Pixelized dot pattern shade. //float c = dot(col, vec3(.299, .587, .114)); vec3 fire = pow(vec3(1.5, 1, 1)*col, vec3(8, 2, 1.5));//*.5+c*.5; col = min(mix(vec3(1, .9, .375), vec3(.75, .375, .3), fire), 2.)*.5 + col;// refl = .65; //dif *= 1.5; //spe2 *= 1.5; } // Combining everything together to produce the scene color. col = col*(dif + .35 + vec3(.35, .45, .5)*spe) + vec3(.7, .9, 1)*spe2 + rCol*refl; col *= occ*atten; // Applying occlusion. } // Applying some very slight fog in the distance. This is technically an inside scene... // Or is it underground... Who cares, it's just a shader. :) col = mix(min(col, 1.), vec3(0), 1.-exp(-t*t/FAR/FAR*20.));//smoothstep(0., FAR-20., t) //col = mix(min(col, 1.), vec3(0), smoothstep(0., FAR-35., t));//smoothstep(0., FAR-20., t) // Done. fragColor = vec4(sqrt(max(col, 0.)), 1.0); } void main(void) { //just some shit to wrap shadertoy's stuff vec2 FragCoord = vTexCoord.xy*global.OutputSize.xy; FragCoord.y = -FragCoord.y; mainImage(FragColor,FragCoord); }