getIBLGGXFresnel: "\n// multi scattering equations\n// not used for now since our IBL GGX Fresnel already handles energy conseervation\n// could be used if we dropped the environment map LUT texture\nfn DFGApprox(\n normal: vec3f,\n viewDirection: vec3f,\n roughness: f32,\n) -> vec2f {\n let dotNV: f32 = saturate(dot( normal, viewDirection ));\n\n\tlet c0: vec4f = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tlet c1: vec4f = vec4( 1, 0.0425, 1.04, - 0.04 );\n\n\tlet r: vec4f = roughness * c0 + c1;\n\tlet a004: f32 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\t\n\tlet fab: vec2f = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\n\treturn fab;\n}\n\nstruct IBLGGXFresnel {\n FssEss: vec3f,\n FmsEms: vec3f\n}\n\nstruct TotalScattering {\n single: vec3f,\n multi: vec3f,\n}\n\nfn computeMultiscattering(\n normal: vec3f,\n viewDirection: vec3f,\n specularColor: vec3f,\n f90: f32,\n roughness: f32,\n ptr_totalScattering: ptr<function, IBLGGXFresnel>\n) {\n let fab: vec2f = DFGApprox( normal, viewDirection, roughness );\n\n\tlet Fr: vec3f = specularColor;\n\n\tlet FssEss: vec3f = Fr * fab.x + f90 * fab.y;\n\n\tlet Ess: f32 = fab.x + fab.y;\n\tlet Ems: f32 = 1.0 - Ess;\n\n\tlet Favg: vec3f = Fr + ( 1.0 - Fr ) * 0.047619; // 1/21\n\tlet Fms: vec3f = FssEss * Favg / ( 1.0 - Ems * Favg );\n\n\t(*ptr_totalScattering).FssEss += FssEss;\n\t(*ptr_totalScattering).FmsEms += Fms * Ems;\n}\n\nfn getIBLGGXFresnel(\n normal: vec3f,\n viewDirection: vec3f,\n roughness: f32,\n f0: vec3f,\n specularWeight: f32,\n clampSampler: sampler,\n lutTexture: texture_2d<f32>\n) -> IBLGGXFresnel {\n var iBLGGXFresnel: IBLGGXFresnel;\n \n let NdotV: f32 = saturate(dot(normal, viewDirection));\n \n let brdfSamplePoint: vec2f = saturate(vec2(NdotV, roughness));\n \n let brdf: vec3f = textureSample(\n lutTexture,\n clampSampler,\n brdfSamplePoint\n ).rgb;\n \n let Fr: vec3f = max(vec3(1.0 - roughness), f0) - f0;\n let k_S: vec3f = f0 + Fr * pow(1.0 - NdotV, 5.0);\n iBLGGXFresnel.FssEss = specularWeight * (k_S * brdf.x + brdf.y);\n let Ems: f32 = (1.0 - (brdf.x + brdf.y));\n let F_avg: vec3f = specularWeight * (f0 + (1.0 - f0) / 21.0);\n iBLGGXFresnel.FmsEms = Ems * iBLGGXFresnel.FssEss * F_avg / (1.0 - F_avg * Ems);\n \n return iBLGGXFresnel;\n}\n" = ...

Helper to implement multi-scattering that compensates for the energy loss in rough surfaces due to multiple reflections. The IBL LUT texture is used to compute multi-scattering in getIBLGGXFresnel, however we could fall back to manually calculate it in case it's missing.