boomblog

My online presence

Below is my implementation and slight modification of the perlin noise based shader described on this excellent tutorial.

You create a custom shader by declaring a THREE.ShaderMaterial and initialize it with the following properties:

  • uniforms - these values are constant throughout the frame
  • attributes - this is a property with a defined value for each vertex
  • vertexShader - define position of the vertex in screen space
  • fragmentShader - take the values defined for the vertices and interpolate for points on the shaded polygon
material = new THREE.ShaderMaterial( {

    uniforms: { 
        tExplosion: { type: "t", value: THREE.ImageUtils.loadTexture( '/assets/img/explosion.png' ) },
        time: { type: "f", value: 0.0 },
        weight: { type: "f", value: 10.0 }
    },
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent
    
} );

The vertex shader here displaces each vertex along the pre-deformation normal scaled by a factor defined by the value of the perlin noise at that point. It uses the uv coordinates of the polygon to map the perlin space to the sphere surface.

// insert contents of https://github.com/ashima/webgl-noise/blob/master/src/classicnoise3D.glsl

varying vec2 vUv;
varying vec3 vReflect;
varying vec3 pos;
varying float ao;
uniform float time;
uniform float weight;
varying float d;

float stripes( float x, float f) {
    float PI = 3.14159265358979323846264;
    float t = .5 + .5 * sin( f * 2.0 * PI * x);
    return t * t - .5;
}

float turbulence( vec3 p ) {
    float w = 100.0;
    float t = -.5;
    for (float f = 1.0 ; f <= 10.0 ; f++ ){
        float power = pow( 2.0, f );
        t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );
    }
    return t;
}

void main() {
    vUv = uv;

    vec4 mPosition = modelMatrix * vec4( position, 1.0 );
    vec3 nWorld = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );
    vReflect = normalize( reflect( normalize( mPosition.xyz - cameraPosition ), nWorld ) );
    
    pos = position;
    // float noise = .3 * pnoise( 8.0 * vec3( normal ) );
    float noise = 10.0 *  -.10 * turbulence( .5 * normal + time );
    // float noise = - stripes( normal.x + 2.0 * turbulence( normal ), 1.6 );

    float displacement = - weight * noise;
    displacement += 5.0 * pnoise( 0.05 * position + vec3( 2.0 * time ), vec3( 100.0 ) );
    
    ao = noise;
    vec3 newPosition = position + normal * vec3( displacement );
    gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}

Do some more funky math, honestly I found all this a little hard to follow.

varying vec2 vUv;
uniform sampler2D tExplosion;
varying vec3 vReflect;
varying vec3 pos;
varying float ao;
varying float d;
float PI = 3.14159265358979323846264;

float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}

void main() {
    vec2 uv = vec2( 0, 1.3 * ao + .01 * random(vec3(12.9898,78.233,151.7182),0.0) );
    uv = clamp( uv, vec2( 0. ), vec2( 1. ) );
    vec3 color = texture2D( tExplosion, uv ).rgb;
    gl_FragColor = vec4( color.rgb, 1.0 );
}