// CONSTANTS const float PI = 3.14159265359; const float MAX_REFLECTION_LOD = 4.0; // FUNCTIONS vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); } vec3 ambientGGX( in vec3 albedoColor, in float metallic, in float roughness, in vec3 N, in vec3 V, in mat3 viewToWorld, in samplerCube irradianceMap, in samplerCube radianceMap, in sampler2D lut) { float NdotV = max(dot(N, V), 0.0); vec3 R = reflect(-V, N); vec3 albedo = pow(albedoColor, vec3(2.2)); // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 // of 0.04 and if it's a metal, use their albedo color as F0 (metallic workflow) vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); // ambient lighting (we now use IBL as the ambient term) (fresnelSchlickRoughness function) vec3 F = fresnelSchlickRoughness(NdotV, F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; vec3 irradiance = texture(irradianceMap, viewToWorld * N).rgb; vec3 diffuse = irradiance * albedo; // sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part. vec3 prefilteredColor = textureLod(radianceMap, viewToWorld * R, roughness * MAX_REFLECTION_LOD).rgb; vec2 brdf2 = texture(lut, vec2(NdotV, roughness)).rg; vec3 specular = prefilteredColor * (F * brdf2.x + brdf2.y); // combining light return kD * diffuse + specular; } float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; float a2 = a*a; float NdotH = max(dot(N, H), 0.0); float denom = (NdotH * NdotH * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return a2 / denom; } float GeometrySchlickGGX(float cosAlpha, float roughness) { float r = (roughness + 1.0); float k = (r*r) / 8.0; return cosAlpha / (cosAlpha * (1.0 - k) + k); } vec3 GGX( in vec3 albedoColor, in float metallic, in float roughness, in vec3 radiance, in vec3 N, in vec3 L, in vec3 H, in vec3 V) { vec3 albedo = pow(albedoColor, vec3(2.2)); vec3 F0 = mix(vec3(0.04), albedo, metallic); float NdotL = max(dot(N, L), 0.0); float NdotV = max(dot(N, V), 0.0); // Cook-Torrance BRDF float NDF = DistributionGGX(N, H, roughness); float G = GeometrySchlickGGX(NdotV, roughness) * GeometrySchlickGGX(NdotL, roughness); vec3 F = F0 + (1.0 - F0) * pow(1.0 - clamp(dot(H, V), 0.0, 1.0), 5.0); vec3 nominator = NDF * G * F; float denominator = 4 * NdotV * NdotL + 0.001; // 0.001 to prevent divide by zero. vec3 brdf = nominator / denominator; vec3 kD = 1.0 - F; kD *= 1.0 - metallic; return (kD * albedo / PI + brdf) * radiance * NdotL; }