     return height;
 }
 
-vec3 get_normal( vec2 p, float epsilon )
+vec3 get_normal( vec2 p, float detailed_height, float epsilon )
 {
     vec3 n;
-    float original = -height_detailed( p );
+    float original = -detailed_height;
     n.x = -height_detailed( vec2( p.x + epsilon, p.y ) ) - original;
     n.z = -height_detailed( vec2( p.x, p.y + epsilon ) ) - original;
     n.y = epsilon;

}

void main( void ) {
    float dist = length( cPosition_World - vPosition_World );
    vec3 view = normalize( -( vPosition.xyz / vPosition.w ) );

    vec3 normal = normalize( uNormalMVMatrix * get_normal( vPosition_World.xz * sea_scale, dist * 0.001 ) );

    vec3 reflected = uInverseViewMatrix * ( -reflect( view, normal ) );
    vec4 ibl_specular = engamma( textureCube( environment, reflected ) * 0.9 );

    return height;
}

float foam( vec2 pos, float detailed_height )
{
    // foaminess is modulated by height
    float base_foaminess = detailed_height;

    float detail = foam_detail( pos );
   
    // poke some holes in foam to simulate appearance of bubbles
    // first term is large bubbles (to reduce noticeable artifacting at close range, we reduce large bubble alpha)
    // second term is tiny bubbles (no noticeable artifacting unless at a particular distance, but that's a sampling artifact)
    float fizziness = clamp( abs( noise( pos * 25.0 ) ) * 0.04 + abs( noise( pos * 380.0 ) ) * 0.2, 0.0, 1.0 );

    // the smoothstep is actually quite important in producing the end result. We purposefully (artistically :) pick
    // a range in the produced noise that looks reasonably passable as foam
