     vec3 cspec = mix(vec3(1.0), baseColor, metallic);
 
     return BRDF(N, L, V, baseColor, metallic, roughness, reflectivity, cdiff, cspec);
+}
+
+vec2 Hammersley(uint i, uint N)
+{
+    return vec2(float(i) / float(N), float(bitfieldReverse(i)) * 2.3283064365386963e-10);
+}
+
+// Computes the exact mip-map to reference for the specular contribution.
+// Accessing the proper mip-map allows us to approximate the integral for this
+// angle of incidence on the current object.
+float compute_lod(uint NumSamples, float NoH, float roughness)
+{
+    // Distribution
+    float D = BeckmannDistribution(NoH, roughness);
+
+    uvec2 Dimensions = uvec2(2048, 2048);
+    return 0.5 * (log2(float(Dimensions.x * Dimensions.y) / NumSamples) - log2(D));
+}
+
+float randAngle()
+{
+    uint x = uint(gl_FragCoord.x);
+    uint y = uint(gl_FragCoord.y);
+    return (30u * x ^ y + 10u * x * y);
+}
+
+vec3 MakeSample(float Theta, float Phi)
+{
+    Phi += randAngle();
+    float SineTheta = sin(Theta);
+
+    float x = cos(Phi) * SineTheta;
+    float y = sin(Phi) * SineTheta;
+    float z = cos(Theta);
+
+    return vec3(x, y, z);
+}
+
+// Calculates the specular influence for a surface at the current fragment
+// location. This is an approximation of the lighting integral itself.
+vec3 radiance(vec3 N, vec3 V, float metallic, float roughness)
+{
+    // Helper variables
+    vec3 ZAxis = vec3(0.0, 0.0, 1.0);
+    vec3 XAxis = vec3(1.0, 0.0, 0.0);
+    float pi2  = pi * pi;
+
+    // Precalculate rotation for +Z Hemisphere to microfacet normal.
+    vec3 UpVector = abs(N.z) < 0.999 ? ZAxis : XAxis;
+    vec3 TangentX = normalize(cross( UpVector, N ));
+    vec3 TangentY = cross(N, TangentX);
+
+    // Note: I ended up using abs() for situations where the normal is
+    // facing a little away from the view to still accept the approximation.
+    // I believe this is due to a separate issue with normal storage, so
+    // you may only need to saturate() each dot value instead of abs().
+    float NoV = abs(dot(N, V));
+
+    // Approximate the integral for lighting contribution.
+    vec3 fColor = vec3(0.0);
+    const uint NumSamples = 10;
+    for (uint i = 0; i < NumSamples; ++i)
+    {
+        vec2 Xi = Hammersley(i, NumSamples);
+
+        // Distribution
+        float r2 = roughness * roughness;
+        float theta = atan(sqrt((r2 * Xi.x) / (1.0 - Xi.x)));
+        float phi   = pi2 * Xi.y;
+        vec3 Li = MakeSample(theta, phi);
+
+        vec3 H  = normalize(Li.x * TangentX + Li.y * TangentY + Li.z * N);
+        vec3 L  = normalize(-reflect(V, H));
+
+        // Calculate dot products for BRDF
+        float NoL = abs(dot(N, L));
+        float NoH = abs(dot(N, H));
+        float VoH = abs(dot(V, H));
+        float lod = compute_lod(NumSamples, NoH, roughness);
+
+        // Fresnel
+        float Kmetallic = metallic;
+        float F = Kmetallic + (1.0 - Kmetallic) * pow(1.0 - VoH, 5.0);
+
+        // Geometrical Attenuation
+        float k = r2 * sqrt(pi2);
+        float G = NoV / (NoV * (1.0 - k) + k);
+
+        // Calculate Color
+        vec3 LColor = textureLod(skybox, L, lod).rgb;
+
+        // Since the sample is skewed towards the Distribution, we don't need
+        // to evaluate all of the factors for the lighting equation. Also note
+        // that this function is calculating the specular portion, so we absolutely
+        // do not add any more diffuse here.
+        fColor += F * G * LColor * VoH / (NoH * NoV);
+    }
+
+    // Average the results
+    return fColor / float(NumSamples);
 }
 
 vec3 CalcLight(vec3 lightColor, vec3 normal, vec3 lightDir, vec3 viewDir, Material material, float shadowFactor)

#module lighting
#extension GL_ARB_texture_query_levels : enable

uniform samplerCube skybox;
uniform samplerCube irrMap;

    vec3 reflectDir = reflect(-viewDir, normal);

    // Get radiance and irradiance colors
    //vec3 rad = radiance(normal, viewDir, metallic, roughness);
    vec3 rad = texture(skybox, reflectDir).rgb + texture(skysphere, reflectDir.xy).rgb;
    vec3 irr = texture(irrMap, reflectDir).rgb;
    irr *= material.diffuse;
