New experiment with perlin noise and shadermaterial
A Pen by Victor Vergara on CodePen.
New experiment with perlin noise and shadermaterial
A Pen by Victor Vergara on CodePen.
| doctype html | |
| head | |
| title Perlin noise | Fireball explosion | |
| meta(charset='utf-8') | |
| .container-fluid.fixed-top.header.disable-selection | |
| .row | |
| .col | |
| h1 | |
| strong Blob | |
| //p.small(href='#',role='button') | |
| strong 708.588 POINTS | |
| #container | |
| script#vertexShader(type='x-shader/x-vertex'). | |
| // | |
| // GLSL textureless classic 3D noise "cnoise", | |
| // with an RSL-style periodic variant "pnoise". | |
| // Author: Stefan Gustavson ([email protected]) | |
| // Version: 2011-10-11 | |
| // | |
| // Many thanks to Ian McEwan of Ashima Arts for the | |
| // ideas for permutation and gradient selection. | |
| // | |
| // Copyright (c) 2011 Stefan Gustavson. All rights reserved. | |
| // Distributed under the MIT license. See LICENSE file. | |
| // https://github.com/ashima/webgl-noise | |
| // | |
| vec3 mod289(vec3 x) | |
| { | |
| return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| } | |
| vec4 mod289(vec4 x) | |
| { | |
| return x - floor(x * (1.0 / 289.0)) * 289.0; | |
| } | |
| vec4 permute(vec4 x) | |
| { | |
| return mod289(((x*34.0)+1.0)*x); | |
| } | |
| vec4 taylorInvSqrt(vec4 r) | |
| { | |
| return 1.79284291400159 - 0.85373472095314 * r; | |
| } | |
| vec3 fade(vec3 t) { | |
| return t*t*t*(t*(t*6.0-15.0)+10.0); | |
| } | |
| // Classic Perlin noise | |
| float cnoise(vec3 P) | |
| { | |
| vec3 Pi0 = floor(P); // Integer part for indexing | |
| vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 | |
| Pi0 = mod289(Pi0); | |
| Pi1 = mod289(Pi1); | |
| vec3 Pf0 = fract(P); // Fractional part for interpolation | |
| vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 | |
| vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); | |
| vec4 iy = vec4(Pi0.yy, Pi1.yy); | |
| vec4 iz0 = Pi0.zzzz; | |
| vec4 iz1 = Pi1.zzzz; | |
| vec4 ixy = permute(permute(ix) + iy); | |
| vec4 ixy0 = permute(ixy + iz0); | |
| vec4 ixy1 = permute(ixy + iz1); | |
| vec4 gx0 = ixy0 * (1.0 / 5.0); | |
| vec4 gy0 = fract(floor(gx0) * (1.0 / 5.0)) - 0.5; | |
| gx0 = fract(gx0); | |
| vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); | |
| vec4 sz0 = step(gz0, vec4(0.0)); | |
| gx0 -= sz0 * (step(0.0, gx0) - 0.5); | |
| gy0 -= sz0 * (step(0.0, gy0) - 0.5); | |
| vec4 gx1 = ixy1 * (1.0 / 5.0); | |
| vec4 gy1 = fract(floor(gx1) * (1.0 / 5.0)) - 0.5; | |
| gx1 = fract(gx1); | |
| vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); | |
| vec4 sz1 = step(gz1, vec4(0.0)); | |
| gx1 -= sz1 * (step(0.0, gx1) - 0.5); | |
| gy1 -= sz1 * (step(0.0, gy1) - 0.5); | |
| vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); | |
| vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); | |
| vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); | |
| vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); | |
| vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); | |
| vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); | |
| vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); | |
| vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); | |
| vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); | |
| g000 *= norm0.x; | |
| g010 *= norm0.y; | |
| g100 *= norm0.z; | |
| g110 *= norm0.w; | |
| vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); | |
| g001 *= norm1.x; | |
| g011 *= norm1.y; | |
| g101 *= norm1.z; | |
| g111 *= norm1.w; | |
| float n000 = dot(g000, Pf0); | |
| float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); | |
| float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); | |
| float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); | |
| float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); | |
| float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); | |
| float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); | |
| float n111 = dot(g111, Pf1); | |
| vec3 fade_xyz = fade(Pf0); | |
| vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); | |
| vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); | |
| float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); | |
| return 2.2 * n_xyz; | |
| } | |
| // Classic Perlin noise, periodic variant | |
| float pnoise(vec3 P, vec3 rep) | |
| { | |
| vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period | |
| vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period | |
| Pi0 = mod289(Pi0); | |
| Pi1 = mod289(Pi1); | |
| vec3 Pf0 = fract(P); // Fractional part for interpolation | |
| vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 | |
| vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); | |
| vec4 iy = vec4(Pi0.yy, Pi1.yy); | |
| vec4 iz0 = Pi0.zzzz; | |
| vec4 iz1 = Pi1.zzzz; | |
| vec4 ixy = permute(permute(ix) + iy); | |
| vec4 ixy0 = permute(ixy + iz0); | |
| vec4 ixy1 = permute(ixy + iz1); | |
| vec4 gx0 = ixy0 * (1.0 / 5.0); | |
| vec4 gy0 = fract(floor(gx0) * (1.0 / 5.0)) - 0.5; | |
| gx0 = fract(gx0); | |
| vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); | |
| vec4 sz0 = step(gz0, vec4(0.0)); | |
| gx0 -= sz0 * (step(0.0, gx0) - 0.5); | |
| gy0 -= sz0 * (step(0.0, gy0) - 0.5); | |
| vec4 gx1 = ixy1 * (1.0 / 5.0); | |
| vec4 gy1 = fract(floor(gx1) * (1.0 / 5.0)) - 0.5; | |
| gx1 = fract(gx1); | |
| vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); | |
| vec4 sz1 = step(gz1, vec4(0.0)); | |
| gx1 -= sz1 * (step(0.0, gx1) - 0.5); | |
| gy1 -= sz1 * (step(0.0, gy1) - 0.5); | |
| vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); | |
| vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); | |
| vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); | |
| vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); | |
| vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); | |
| vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); | |
| vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); | |
| vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); | |
| vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); | |
| g000 *= norm0.x; | |
| g010 *= norm0.y; | |
| g100 *= norm0.z; | |
| g110 *= norm0.w; | |
| vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); | |
| g001 *= norm1.x; | |
| g011 *= norm1.y; | |
| g101 *= norm1.z; | |
| g111 *= norm1.w; | |
| float n000 = dot(g000, Pf0); | |
| float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); | |
| float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); | |
| float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); | |
| float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); | |
| float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); | |
| float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); | |
| float n111 = dot(g111, Pf1); | |
| vec3 fade_xyz = fade(Pf0); | |
| vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); | |
| vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); | |
| float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); | |
| return 1.5 * n_xyz; | |
| } | |
| // Turbulence By Jaume Sanchez => https://codepen.io/spite/ | |
| varying vec2 vUv; | |
| varying float noise; | |
| varying float qnoise; | |
| varying float displacement; | |
| uniform float time; | |
| uniform float displace; | |
| uniform float pointscale; | |
| uniform float decay; | |
| uniform float size; | |
| uniform float complex; | |
| uniform float waves; | |
| uniform float eqcolor; | |
| uniform bool fragment; | |
| float turbulence( vec3 p) { | |
| float t = - 0.005; | |
| for (float f = 1.0 ; f <= 1.0 ; f++ ){ | |
| float power = pow( 1.3, f ); | |
| t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power ); | |
| } | |
| return t; | |
| } | |
| void main() { | |
| vUv = uv; | |
| noise = (2.0 * - waves) * turbulence( decay * abs(normal + time)); | |
| qnoise = (0.3 * - eqcolor) * turbulence( decay * abs(normal + time)); | |
| float b = pnoise( complex * (position) + vec3( (decay * 2.0) * time ), vec3( 100.0 ) ); | |
| displacement = - atan(noise) + tan(b * displace); | |
| vec3 newPosition = (position) + (normal * displacement); | |
| gl_Position = (projectionMatrix * modelViewMatrix) * vec4( newPosition, abs(size) ); | |
| gl_PointSize = (3.0); | |
| } | |
| script#fragmentShader(type='x-shader/x-vertex'). | |
| varying float qnoise; | |
| varying float noise; | |
| uniform float time; | |
| uniform bool redhell; | |
| uniform float rcolor; | |
| uniform float gcolor; | |
| uniform float bcolor; | |
| void main() { | |
| float r, g, b; | |
| if (!redhell == true) { | |
| r = sin(qnoise + rcolor); | |
| g = normalize(qnoise + (gcolor / 2.0)); | |
| b = tan(qnoise + bcolor); | |
| } else { | |
| r = normalize(qnoise + rcolor); | |
| g = cos(qnoise + gcolor); | |
| b = sin(qnoise + bcolor); | |
| } | |
| gl_FragColor = vec4(r, g, b, 1.0); | |
| } |
| // Three JS | |
| window.addEventListener('load', init, false); | |
| function init() { | |
| createWorld(); | |
| createGUI(); | |
| createPrimitive(); | |
| animation(); | |
| } | |
| var Theme = { | |
| primary: 0xFFFFFF, | |
| secundary: 0x292733, | |
| danger: 0xFF0000, | |
| darker: 0x000000 | |
| }; | |
| //-------------------------------------------------------------------- | |
| var scene, camera, renderer, container; | |
| var _width, _height; | |
| var _primitive; | |
| var shapeGroup = new THREE.Group(); | |
| var start = Date.now(); | |
| function createWorld() { | |
| _width = window.innerWidth; | |
| _height= window.innerHeight; | |
| //--- | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(Theme.secundary); | |
| //--- | |
| camera = new THREE.PerspectiveCamera(35, _width/_height, 1, 1000); | |
| camera.position.set(0,0,16); | |
| //--- | |
| renderer = new THREE.WebGLRenderer({antialias:false, alpha:false}); | |
| renderer.setSize(_width, _height); | |
| renderer.shadowMap.enabled = true; | |
| //--- | |
| document.body.appendChild(renderer.domElement); | |
| //--- | |
| window.addEventListener('resize', onWindowResize, false); | |
| } | |
| function onWindowResize() { | |
| _width = window.innerWidth; | |
| _height = window.innerHeight; | |
| renderer.setSize(_width, _height); | |
| camera.aspect = _width / _height; | |
| camera.updateProjectionMatrix(); | |
| console.log('- resize -'); | |
| } | |
| //-------------------------------------------------------------------- | |
| var primitiveElement = function() { | |
| this.mesh = new THREE.Object3D(); | |
| mat = new THREE.ShaderMaterial( { | |
| side:THREE.DoubleSide, | |
| uniforms: { | |
| time: { | |
| type: "f", | |
| value: 0.1 | |
| }, | |
| pointscale: { | |
| type: "f", | |
| value: 0.2 | |
| }, | |
| decay: { | |
| type: "f", | |
| value: 0.3 | |
| }, | |
| size: { | |
| type: "f", | |
| value: 0.3 | |
| }, | |
| displace: { | |
| type: "f", | |
| value: 0.3 | |
| }, | |
| complex: { | |
| type: "f", | |
| value: 0.0 | |
| }, | |
| waves: { | |
| type: "f", | |
| value: 0.10 | |
| }, | |
| eqcolor: { | |
| type: "f", | |
| value: 0.0 | |
| }, | |
| rcolor: { | |
| type: "f", | |
| value: 0.0 | |
| }, | |
| gcolor: { | |
| type: "f", | |
| value: 0.0 | |
| }, | |
| bcolor: { | |
| type: "f", | |
| value: 0.0 | |
| }, | |
| fragment: { | |
| type: "i", | |
| value: true | |
| }, | |
| redhell: { | |
| type: "i", | |
| value: true | |
| } | |
| }, | |
| vertexShader: document.getElementById( 'vertexShader' ).textContent, | |
| fragmentShader: document.getElementById( 'fragmentShader' ).textContent | |
| }); | |
| //--- | |
| var wir_mat = new THREE.MeshBasicMaterial({color:Theme.darker}); | |
| var geo = new THREE.IcosahedronBufferGeometry(2, 6); | |
| var wir = new THREE.IcosahedronBufferGeometry(2.3, 2); | |
| this.shape = new THREE.Mesh(geo, mat); | |
| this.point = new THREE.Points(wir, mat); | |
| //--- | |
| shapeGroup.add(this.point); | |
| shapeGroup.add(this.shape); | |
| scene.add(shapeGroup); | |
| } | |
| function createPrimitive() { | |
| _primitive = new primitiveElement(); | |
| } | |
| var options = { | |
| perlin: { | |
| speed: 0.4, | |
| size: 0.7, | |
| perlins: 1.0, | |
| decay: 1.20, | |
| displace: 1.00, | |
| complex: 0.50, | |
| waves: 3.7, | |
| eqcolor: 10.0, | |
| rcolor: 1.5, | |
| gcolor: 1.5, | |
| bcolor: 1.5, | |
| fragment: true, | |
| points: false, | |
| redhell: true | |
| }, | |
| perlinRandom: function() { | |
| TweenMax.to(this.perlin, 2, { | |
| //decay: Math.random() * 1.0, | |
| waves: Math.random() * 20.0, | |
| complex: Math.random() * 1.0, | |
| displace: Math.random() * 2.5, | |
| ease:Elastic.easeOut | |
| }); | |
| }, | |
| random: function() { | |
| //this.perlin.redhell = Math.random() >= 0.5; // 10 1 0.1 1.2 | |
| TweenMax.to(this.perlin, 1, { | |
| eqcolor: 11.0, | |
| rcolor: Math.random() * 1.5, | |
| gcolor: Math.random() * 0.5, | |
| bcolor: Math.random() * 1.5, | |
| ease:Quart.easeInOut | |
| }); | |
| }, | |
| normal: function() { | |
| this.perlin.redhell = true; // 10 1 0.1 1.2 | |
| TweenMax.to(this.perlin, 1, { | |
| //speed: 0.12, | |
| eqcolor: 10.0, | |
| rcolor: 1.5, | |
| gcolor: 1.5, | |
| bcolor: 1.5, | |
| ease:Quart.easeInOut | |
| }); | |
| }, | |
| darker: function() { | |
| this.perlin.redhell = false; // 10 1 0.1 1.2 | |
| TweenMax.to(this.perlin, 1, { | |
| //speed: 0.5, | |
| eqcolor: 9.0, | |
| rcolor: 0.4, | |
| gcolor: 0.05, | |
| bcolor: 0.6, | |
| ease:Quart.easeInOut | |
| }); | |
| }, | |
| volcano: function() { | |
| this.perlin.redhell = false; // 10 1 0.1 1.2 | |
| //this.perlin.speed = 0.83; | |
| TweenMax.to(this.perlin, 1, { | |
| size: 0.7, | |
| waves: 0.6, | |
| complex: 1.0, | |
| displace: 0.3, | |
| eqcolor: 9.0, | |
| rcolor: 0.85, | |
| gcolor: 0.05, | |
| bcolor: 0.32, | |
| ease:Quart.easeInOut | |
| }); | |
| }, | |
| cloud: function() { | |
| this.perlin.redhell = true; // 10 1 0.1 1.2 | |
| //this.perlin.speed = 0.1; | |
| TweenMax.to(this.perlin, 1, { | |
| size: 1.0, | |
| waves :20.0, | |
| complex: 0.1, | |
| displace: 0.1, | |
| eqcolor: 4.0, | |
| rcolor: 1.5, | |
| gcolor: 0.7, | |
| bcolor: 1.5, | |
| ease:Quart.easeInOut | |
| }); | |
| }, | |
| tornasol: function() { | |
| this.perlin.redhell = true; // 10 1 0.1 1.2 | |
| //this.perlin.speed = 0.25; | |
| TweenMax.to(this.perlin, 1, { | |
| size: 1.0, | |
| waves: 3.0, | |
| complex: 0.65, | |
| displace: 0.5, | |
| eqcolor: 9.5, | |
| rcolor: 1.5, | |
| gcolor: 1.5, | |
| bcolor: 1.5, | |
| ease:Quart.easeInOut | |
| }); | |
| } | |
| } | |
| function createGUI() { | |
| var gui = new dat.GUI(); | |
| var perlinGUI = gui.addFolder('Shape Setup'); | |
| perlinGUI.add(options, 'perlinRandom').name('• Random Shape'); | |
| perlinGUI.add(options.perlin, 'speed', 0.1, 1.0).name('Speed').listen(); | |
| perlinGUI.add(options.perlin, 'size', 0.0, 3.0).name('Size').listen(); | |
| //perlinGUI.add(options.perlin, 'decay', 0.0, 1.0).name('Decay').listen(); | |
| perlinGUI.add(options.perlin, 'waves', 0.0, 20.0).name('Waves').listen(); | |
| perlinGUI.add(options.perlin, 'complex', 0.1, 1.0).name('Complex').listen(); | |
| perlinGUI.add(options.perlin, 'displace', 0.1, 2.5).name('Displacement').listen(); | |
| //perlinGUI.open(); | |
| var colorGUI = gui.addFolder('Color'); | |
| colorGUI.add(options, 'random').name('• Random colors'); | |
| colorGUI.add(options, 'normal').name('• Normal colors'); | |
| colorGUI.add(options, 'darker').name('• Dark colors'); | |
| colorGUI.add(options.perlin, 'eqcolor', 0.0, 30.0).name('Hue').listen(); | |
| colorGUI.add(options.perlin, 'rcolor', 0.0, 2.5).name('R').listen(); | |
| colorGUI.add(options.perlin, 'gcolor', 0.0, 2.5).name('G').listen(); | |
| colorGUI.add(options.perlin, 'bcolor', 0.0, 2.5).name('B').listen(); | |
| colorGUI.add(options.perlin, 'redhell', true).name('Electroflow'); | |
| //colorGUI.open(); | |
| gui.add(options, 'volcano').name('• Volcano'); | |
| gui.add(options, 'tornasol').name('• Tornasol'); | |
| gui.add(options, 'cloud').name('• Cotton Candy'); | |
| gui.add(options.perlin, 'points', true).name('Points'); | |
| } | |
| //-------------------------------------------------------------------- | |
| function animation() { | |
| var performance = Date.now() * 0.003; | |
| //--- | |
| //_primitive.shape.visible = !options.perlin.points; | |
| _primitive.point.visible = options.perlin.points; | |
| //--- | |
| mat.uniforms['time'].value = (options.perlin.speed / 1000) * (Date.now() - start); | |
| mat.uniforms['pointscale'].value = options.perlin.perlins; | |
| mat.uniforms['decay'].value = options.perlin.decay; | |
| mat.uniforms['size'].value = options.perlin.size; | |
| mat.uniforms['displace'].value = options.perlin.displace; | |
| mat.uniforms['complex'].value = options.perlin.complex; | |
| mat.uniforms['waves'].value = options.perlin.waves; | |
| mat.uniforms['fragment'].value = options.perlin.fragment; | |
| mat.uniforms['redhell'].value = options.perlin.redhell; | |
| mat.uniforms['eqcolor'].value = options.perlin.eqcolor; | |
| mat.uniforms['rcolor'].value = options.perlin.rcolor; | |
| mat.uniforms['gcolor'].value = options.perlin.gcolor; | |
| mat.uniforms['bcolor'].value = options.perlin.bcolor; | |
| //--- | |
| requestAnimationFrame(animation); | |
| renderer.render(scene, camera); | |
| } |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script> | |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script> |
| $white: #FFFFFF | |
| $black: #000000 | |
| body | |
| color: $white | |
| margin: 0 | |
| text-align: center | |
| background-color: $black | |
| canvas | |
| display: block | |
| width: 100% | |
| height: 100% | |
| p | |
| color: rgba(255,255,255, 0.5) | |
| .header | |
| width: 250px | |
| top: 50% | |
| transform: translatey(-50%) | |
| a, a:hover, a:visited | |
| color: $white | |
| text-decoration: none | |
| h1 | |
| &:after | |
| content: ' ThreeJS' | |
| font-size: 0.6rem | |
| position: relative | |
| top: -1rem |
| <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" /> |