Advertisement
DEKTEN

SHADERTOY/SHANE/SMOOTH_NOISE_CONTOURS

May 4th, 2021
1,026
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Written By: https://www.shadertoy.com/user/Shane
  2. /*
  3.  
  4.     Smooth Noise Contours
  5.     ---------------------
  6.  
  7.     Using a cheap - but effective - hack to produce antialiased-looking contour lines without the
  8.     need for supersampling. I had Airtight's elegant "Cartoon Fire" shader in mind when making this,
  9.     and have provided a link to it below.
  10.  
  11.     I've always liked the abstract look of functional contour segments, lines, etc. There's a couple
  12.     of ways to produce them, depending on the look you're going for. One method involves combining
  13.     your function value (noise, Voronoi, etc) with the "fract" function and the other involves
  14.     stepping the function values with the "floor" function.
  15.  
  16.     Each looks all right, except for the aliasing. You could take care of that with supersampling,
  17.     but it's a lot of work for the GPU, so I figured there might be a way to combine the "smoothstep"
  18.     and "fwidth" functions to produce a smooth "fract" function. Since "x - fract(x)" is "floor(x),"
  19.     you'd get the "floor" function too.
  20.  
  21.     After playing around for a while, I came up with something that seems to work. As you can see,
  22.     the partitioned contours look relatively jaggy free, even after the application of border lines
  23.     and highlighting.
  24.  
  25.     Anyway, the smooth fract "sFract" and complimentary smooth floor "sFloor" functions are below.
  26.     They haven't undergone extensive testing, so I'd use them cautiously. :)
  27.  
  28.     The rest is just coloring and highlighting. I went for a simplistic cardboard cutout, vector-graphic
  29.     style.
  30.  
  31.     Similar examples:
  32.  
  33.     Cartoon Fire - airtight
  34.     https://www.shadertoy.com/view/lsscWr
  35.  
  36.     // More sophisticated smoothing method, but I might switch to this one in future.
  37.     Smooth Voronoi Contours - Shane
  38.     https://www.shadertoy.com/view/4sdXDX
  39.  
  40.  
  41. */
  42.  
  43.  
  44. // Variable to a keep a copy of the noise value prior to palettization. Used to run a soft gradient
  45. // over the surface, just to break things up a little.
  46. float ns;
  47.  
  48.  
  49. //float sFract(float x, float sm){ float fx = fract(x); return fx - smoothstep(fwidth(x)*sm, 0., 1. - fx); }
  50. //float sFract(float x, float sm){ float fx = fract(x); return min(fx, fx*(1. - fx)/fwidth(x)/sm); }
  51.  
  52. // Based on Ollj's smooth "fract" formula.
  53. float sFract(float x, float sm){
  54.    
  55.     // Extra smoothing factor. "1" is the norm.
  56.     const float sf = 1.;
  57.    
  58.     // The hardware "fwidth" is cheap, but you could take the expensive route and
  59.     // calculate it by hand if more quality was required.
  60.     vec2 u = vec2(x, fwidth(x)*sf*sm);
  61.    
  62.     // Ollj's original formula with a transcendental term omitted.
  63.     u.x = fract(u.x);
  64.     u += (1. - 2.*u)*step(u.y, u.x);
  65.     return clamp(1. - u.x/u.y, 0., 1.); // Cos term ommitted.
  66. }
  67.  
  68.  
  69.  
  70. // Only correct for nonnegative values, but in this example, numbers aren't negative.
  71. float sFloor(float x){ return x - sFract(x, 1.); }
  72.  
  73. // Standard hue rotation formula with a bit of streamlining.
  74. vec3 rotHue(vec3 p, float a){
  75.  
  76.     vec2 cs = sin(vec2(1.570796, 0) + a);
  77.  
  78.     mat3 hr = mat3(0.299,  0.587,  0.114,  0.299,  0.587,  0.114,  0.299,  0.587,  0.114) +
  79.               mat3(0.701, -0.587, -0.114, -0.299,  0.413, -0.114, -0.300, -0.588,  0.886) * cs.x +
  80.               mat3(0.168,  0.330, -0.497, -0.328,  0.035,  0.292,  1.250, -1.050, -0.203) * cs.y;
  81.                              
  82.     return clamp(p*hr, 0., 1.);
  83. }
  84.  
  85.  
  86. /*
  87. // Fabrices concise, 2D rotation formula.
  88. mat2 r2(float th){ vec2 a = sin(vec2(1.5707963, 0) + th); return mat2(a, -a.y, a.x); }
  89.  
  90. // Dave's hash function. More reliable with large values, but will still eventually break down.
  91. //
  92. // Hash without Sine
  93. // Creative Commons Attribution-ShareAlike 4.0 International Public License
  94. // Created by David Hoskins.
  95. // vec3 to vec3.
  96. vec3 hash33(vec3 p){
  97.  
  98.     p = fract(p * vec3(.1031, .1030, .0973));
  99.     p += dot(p, p.yxz + 19.19);
  100.     p = fract((p.xxy + p.yxx)*p.zyx)*2. - 1.;
  101.     return p;
  102.    
  103.     // Note the "mod" call. Slower, but ensures accuracy with large time values.
  104.     //mat2  m = r2(mod(iTime*2., 6.2831853));  
  105.     //p.xy = m * p.xy;//rotate gradient vector
  106.     //p.yz = m * p.yz;//rotate gradient vector
  107.     //p.xz = m * p.xz;//rotate gradient vector
  108.    
  109.     //mat3 m = r3(mod(iTime*2., 6.2831853));   
  110.     //vec3 th = mod(vec3(.31, .53, .97) + iTime*2., 6.2831853);
  111.     //mat3 m = r3(th.x, th.y, th.z);
  112.     //p *= m;
  113.     return p;
  114.  
  115. }
  116. */
  117.  
  118. // vec3 to vec3 hash algorithm.
  119. vec3 hash33(vec3 p) {
  120.  
  121.     // Faster, but doesn't disperse things quite as nicely as the block below it. However, when framerate
  122.     // is an issue, and it often is, this is the one to use. Basically, it's a tweaked amalgamation I put
  123.     // together, based on a couple of other random algorithms I've seen around... so use it with caution,
  124.     // because I make a tonne of mistakes. :)
  125.     float n = sin(dot(p, vec3(7, 157, 113)));    
  126.     return fract(vec3(2097152, 262144, 32768)*n)*2. - 1.; // return fract(vec3(64, 8, 1)*32768.0*n)*2.-1.;
  127.  
  128.     // I'll assume the following came from IQ.
  129.     //p = vec3( dot(p, vec3(127.1, 311.7, 74.7)), dot(p, vec3(269.5, 183.3, 246.1)), dot(p, vec3(113.5, 271.9, 124.6)));
  130.     //return (fract(sin(p)*43758.5453)*2. - 1.);
  131.  
  132. }
  133.  
  134.  
  135.  
  136. // Cheap, streamlined 3D Simplex noise... of sorts. I cut a few corners, so it's not perfect, but it's
  137. // artifact free and does the job. I gave it a different name, so that it wouldn't be mistaken for
  138. // the real thing.
  139. //
  140. // Credits: Ken Perlin, the inventor of Simplex noise, of course. Stefan Gustavson's paper -
  141. // "Simplex Noise Demystified," IQ, other "ShaderToy.com" people, etc.
  142. float tetraNoise(in vec3 p)
  143. {
  144.     // Skewing the cubic grid, then determining the first vertice and fractional position.
  145.     vec3 i = floor(p + dot(p, vec3(.333333)) );  p -= i - dot(i, vec3(.166666)) ;
  146.    
  147.     // Breaking the skewed cube into tetrahedra with partitioning planes, then determining which side of the
  148.     // intersecting planes the skewed point is on. Ie: Determining which tetrahedron the point is in.
  149.     vec3 i1 = step(p.yzx, p), i2 = max(i1, 1. - i1.zxy); i1 = min(i1, 1. - i1.zxy);    
  150.    
  151.     // Using the above to calculate the other three vertices -- Now we have all four tetrahedral vertices.
  152.     // Technically, these are the vectors from "p" to the vertices, but you know what I mean. :)
  153.     vec3 p1 = p - i1 + .166666, p2 = p - i2 + .333333, p3 = p - .5;
  154.  
  155.  
  156.     // 3D simplex falloff - based on the squared distance from the fractional position "p" within the
  157.     // tetrahedron to the four vertice points of the tetrahedron.
  158.     vec4 v = max(.5 - vec4(dot(p, p), dot(p1, p1), dot(p2, p2), dot(p3, p3)), 0.);
  159.    
  160.     // Dotting the fractional position with a random vector, generated for each corner, in order to determine
  161.     // the weighted contribution distribution... Kind of. Just for the record, you can do a non-gradient, value
  162.     // version that works almost as well.
  163.     vec4 d = vec4(dot(p, hash33(i)), dot(p1, hash33(i + i1)), dot(p2, hash33(i + i2)), dot(p3, hash33(i + 1.)));
  164.      
  165.      
  166.     // Simplex noise... Not really, but close enough. :)
  167.     return clamp(dot(d, v*v*v*8.)*1.732 + .5, 0., 1.); // Not sure if clamping is necessary. Might be overkill.
  168.  
  169. }
  170.  
  171.  
  172. // The function value. In this case, slightly-tapered, quantized Simplex noise.
  173. float func(vec2 p){
  174.    
  175.     // The noise value.
  176.     float n = tetraNoise(vec3(p.x*4., p.y*4., 0) - vec3(0, .25, .5)*iTime);
  177.    
  178.     // A tapering function, similar in principle to a smooth combine. Used to mutate or shape
  179.     // the value above. This one tapers it off into an oval shape and punches in a few extra holes.
  180.     // Airtight uses a more interesting triangular version in his "Cartoon Fire" shader.
  181.     float taper = .1 + dot(p, p*vec2(.35, 1));
  182.     n = max(n - taper, 0.)/max(1. - taper, .0001);
  183.    
  184.     // Saving the noise value prior to palettization. Used for a bit of gradient highlighting.
  185.     ns = n;
  186.    
  187.     // I remember reasoning to myself that the following would take a continuous function ranging
  188.     // from zero to one, then palettize it over "palNum" discreet values between zero and one
  189.     // inclusive. It seems to work, but if my logic is lacking (and it often is), feel free to
  190.     // let me know. :)
  191.     const float palNum = 9.;
  192.     // The range should strictly fall between zero and one, but for some crazy reason, numbers fall
  193.     // outside the range, so I've had to clamp it. I know the computer is never wrong, so I'm
  194.     // probably overlooking something. Having said that, I don't trust the GPU "fract" function much.
  195.     //return clamp(sFloor(n*(palNum - .001))/(palNum - 1.), 0., 1.);
  196.     return n*.25 + clamp(sFloor(n*(palNum - .001))/(palNum - 1.), 0., 1.)*.75;
  197.    
  198. }
  199.  
  200.  
  201.  
  202. void mainImage(out vec4 fragColor, in vec2 fragCoord){
  203.  
  204.     // Screen coordinates.
  205.     vec2 u = (fragCoord.xy - iResolution.xy*.5)/iResolution.y;
  206.    
  207.     // Function value.
  208.     float f = func(u);
  209.     float ssd = ns; // Saving the unpalettized noise value to add a little gradient to the color, etc.
  210.    
  211.     // Four sample values around the original. Used for edging and highlighting.
  212.     vec2 e = vec2(1.5/iResolution.y, 0);
  213.     float fxl = func(u + e.xy);
  214.     float fxr = func(u - e.xy);
  215.     float fyt = func(u + e.yx);
  216.     float fyb = func(u - e.yx);
  217.    
  218.     // Colorizing the function value, and applying some hue rotation based on position.
  219.     // Most of it was made up.
  220.     vec3 col = pow(min(vec3(1.5, 1, 1)*(f*.7 + ssd*.35), 1.), vec3(1, 2., 10)*2.) + .01;
  221.     col = rotHue(col, -.25+.4*length(u));
  222.  
  223.     // Applying the dark edges.
  224.     col *= max(1. - (abs(fxl - fxr) + abs(fyt - fyb))*5., 0.);
  225.     //col *= max(1. - length(vec2(fxl, fyt) - vec2(fxr, fyb))*7., 0.);
  226.     // Resampling with a slightly larger spread to provide some highlighting.
  227.     fxl = func(u + e.xy*1.5);
  228.     fyt = func(u + e.yx*1.5);
  229.     col += vec3(.5, .7, 1)*(max(f - fyt, 0.) + max(f - fxl, 0.))*ssd*10.;
  230.    
  231.     // Subtle, bluish vignette.
  232.     //u = fragCoord/iResolution.xy;
  233.     //col = mix(vec3(0, .1, 1), col, pow( 16.0*u.x*u.y*(1.0-u.x)*(1.0-u.y) , .125)*.15 + .85);
  234.  
  235.    
  236.     // Rough gamma correction.
  237.     fragColor = vec4(sqrt(clamp(col, 0., 1.)), 1);
  238.    
  239. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement