let PI: f32 = 3.141592; fn D_GGX(NoH: f32, roughness: f32) -> f32 { let a = roughness * roughness; let a2 = a * a; let NoH2 = NoH * NoH; let f = NoH * (a2 - 1.0) + 1.0; return a2 / (PI * f * f); } fn g1(NoV: f32, roughness: f32, k: f32) -> f32 { let denom = NoV * (1.0 - k) + k; return NoV / denom; } fn G_SmithGGXCorrelated(NoV: f32, NoL: f32, roughness: f32) -> f32 { let r = roughness + 1.0; let k = (r * r) / 8.0; let g1l = g1(NoV, roughness, k); let g1v = g1(NoL, roughness, k); return g1l * g1v; } fn F_Schlick(u: f32, f0: vec3) -> vec3 { let f = pow(1.0 - u, 5.0); return f + f0 * (1.0 - f); } fn BRDF( l: vec3, // normalized light direction n: vec3, // normalized surface normal v: vec3, // normalized view direction albedo: vec3, // surface albedo metallic: f32, // surface metallic roughness: f32, // surface roughness ) -> vec3 { let h = normalize(v + l); let NoL = max(dot(n, l), 0.0); let NoV = max(dot(n, v), 0.0); let NoH = max(dot(n, h), 0.0); let LoH = max(dot(l, h), 0.0); // calculate reflectance at surface incidence let f0 = mix(vec3(0.04), albedo, metallic); // specular BRDF let D = D_GGX(NoH, roughness); let G = G_SmithGGXCorrelated(NoV, NoL, roughness); let F = F_Schlick(LoH, f0); let numerator = (D * G) * F; let denominator = 4.0 * NoV * NoL; let Fr = numerator / max(denominator, 0.01); // diffuse BRDF let diffuse_fresnel = (vec3(1.0) - F_Schlick(NoL, f0)) * (vec3(1.0) - F_Schlick(NoV, f0)); let lambertian = albedo / PI; let Fd = diffuse_fresnel * lambertian; // TODO multiple scattering return (Fr + Fd) * NoL; }