URL: http://glslsandbox.com/e#27370.3
Notes:
Noise is a fundamental building block for generating interesting textures procedurally. Noise can be used to map colors, generate bump maps, serve as offsets for other texture patterns, and more! So having a good understanding and insight into how to generate noise is very important. In this shader, I explain just how to do that!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
// GLSL Noise Tutorial // By: Brandon Fogerty // bfogerty at gmail dot com // xdpixel.com // Special thanks to IQ and Scratch a Pixel! // Noise can be used to generate various kinds of effects in computer graphics. // Learning how to generate noise in a shader will help you greatly in // creating cool glsl shader effects! #ifdef GL_ES precision mediump float; #endif uniform float time; uniform vec2 mouse; uniform vec2 resolution; // GLSL doesn't have a random function. So we have to create one ourself. // We would like a random number somewhere between 0.0 and 1.0. // We can get that by using the fract function which returns the fractional // component of a number. The constant numbers in this function don't matter // too much. They just need to be random. float hash( vec2 p ) { return fract( sin( dot(p, vec2( 15.79, 81.93 ) ) * 45678.9123 ) ); } // Our valueNoise function will bilinearly interpolate a lattice (aka grid) // and return a smoothed value. This function will essentially allow us to generate // 2D static. Bilinear interpolation basically allows us to transform our 1D hash function to a value based on a 2D grid. // This will eventually be run through an fbm to help us generate a // cloud like pattern. // For more information about bilinear filtering, check out Scratch A Pixel's article. // http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/interpolation/bilinear-interpolation/ // For more info on Value based noise check this url out // http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/noise-part-1/creating-a-simple-2d-noise/ float valueNoise( vec2 p ) { // i is an integer that allow us to move along grid points. vec2 i = floor( p ); // f will be used as an offset between the grid points. vec2 f = fract( p ); // Hermite Curve. // The formula 3f^2 - 2f^3 generates an S curve between 0.0 and 1.0. // If we factor out the variable f, we get f*f*(3.0 - 2.0*f) // This allows us to smoothly interpolate along an s curve between our grid points. // To see the S curve graph, go to the following url. // https://www.desmos.com/calculator/mnrgw3yias f = f*f*(3.0 - 2.0*f); // Interpolate the along the bottom of our grid. float bottomOfGrid = mix( hash( i + vec2( 0.0, 0.0 ) ), hash( i + vec2( 1.0, 0.0 ) ), f.x ); // Interpolate the along the top of our grid. float topOfGrid = mix( hash( i + vec2( 0.0, 1.0 ) ), hash( i + vec2( 1.0, 1.0 ) ), f.x ); // We have now interpolated the horizontal top and bottom grid lines. // We will now interpolate the vertical line between those 2 horizontal points // to get our final value for noise. float t = mix( bottomOfGrid, topOfGrid, f.y ); return t; } // fbm stands for "Fractional Brownian Motion". // Essentially this function calls our valueNoise function multiple // times and adds up the results. By adding various frequences of noise // at different amplitudes, we can generate a simple cloud like pattern. float fbm( vec2 uv ) { float sum = 0.00; float amp = 0.7; for( int i = 0; i < 4; ++i ) { sum += valueNoise( uv ) * amp; uv += uv * 1.2; amp *= 0.4; } return sum; } // This is where everything starts! void main( void ) { // gl_FragCoord.xy is the coordinate of the current pixel being rendered. // It is in screen space. For example if you resolution is 800x600, gl_FragCoord.xy // could be (300,400). By dividing the fragcoord by the resolution, we get normalized // coordinates between 0.0 and 1.0. I would like to work in a -1.0 to 1.0 space // so I multiply the result by 2.0 and subtract 1.0 from it. // if (gl_FragCoord.xy / resolution.xy) equals 0.0, then 0.0 * 2.0 - 1.0 = -1.0 // if (gl_FragCoord.xy / resolution.xy) equals 1.0, then 1.0 * 2.0 - 1.0 = 1.0 vec2 uv = ( gl_FragCoord.xy / resolution.xy ) * 2.0 - 1.0; // I am assuming you have more pixels horizontally than vertically so I am multiplying // the x coordinate by the aspect ratio. This means that the magnitude of x coordinate will probably // be larger than 1.0. This allows our image to not look squashed. uv.x *= resolution.x / resolution.y; // Pass in a coordinate and get a cloud based scalar value back. // We will use this value to generate a gray-scale based color to display our cloud. float t = fbm( uv * 2.0 ); vec3 finalColor = vec3( t, t, t ); // If you want to add color to your cloud, multiply each t red/green/blue component by some weight. // You can uncomment the following line to see and example. //finalColor = vec3( t * 2.0, t * 4.0, t * 8.0 ) * 0.3; // And voila! We are done! We should now have a cloud! =D // gl_FragColor is the final color we want to render for whatever pixel we are currently rendering. gl_FragColor = vec4( finalColor, 1.0 ); } |