 	vec3 shift = vec3( 100 );
     const int NUM_OCTAVES = 5;
 	for (int i = 0; i < NUM_OCTAVES; ++i) {
-		v += a * noise( x );
-		x = x * 2.0 + shift;
+        v += 0.75 * a * noise( x );
+        // modulate with Worley noise to produce billowy shapes
+        v += 0.50 * a * ( 1.0 - worley( x * 2.5, 1.0 ) );
+        x = x * 2.0 + shift;
 		a *= 0.5;
 	}
 	return v;

const   float sky_saturation = 0.7;       // how blue should the sky be (if we look straight up)
const   float sky_horizon_offset = -0.3;  // between -1 and 1, moves horizon down if negative, moves horizon up if positive 

const   float cloudiness = 0.2;
const   float cloud_speed = 10.0;

// noise functions by Inigo Quilez
float hash(float n) { return fract(sin(n) * 1e4); }

    vec2 ofs = vec2( uTime * cloud_speed * 80.0, uTime * cloud_speed * 60.0 );
    vec4 acc = vec4( 0, 0, 0, 0 );

    // early exit if we're beneath a certain threshold
    // this doesn't seem to save any frames, though
    // if ( dot( vec3( 0.0, 1.0, 0.0 ), v ) < 0.0 ) return acc;

    const int layers = int( float( 60 ) * cloudiness );
    for ( int i = 0; i < layers; i++ ) {
        float height = ( float( i ) * 12.0 + 200.0 - cPosition_World.y ) / v.y;
        vec3 cloudPos = cPosition_World.xyz + height*v + vec3( 831.0, 321.0 + float( i ) * 0.15 - 0.2*ofs.x, 1330.0 + 0.3*ofs.y );
        float density = cloudiness * smoothstep( 0.5, 1.0, fbm( cloudPos * 0.0015 ) );
        vec3  color = mix( vec3( 1.1, 1.05, 1.0 ), cloud_base_color, density );

        density = ( 1.0 - acc.w ) * density;
        acc += vec4( color * density, density );
