#ifndef __BRDF_INC #define __BRDF_INC #include "pema99.cginc" #include "pbr.cginc" #include "lighting.cginc" #include "lysenko.cginc" #include "math.cginc" float pow5(float x) { float x2 = x * x; return x2 * x2 * x; } float Fd_Lambertian(float NoL) { return NoL; } // Schlick "An Inexpensive BRDF Model for Physically-based Rendering". // Equation 24. // f0: Reflectance at normal incidence. Typically around 0.04. // f90: Reflectance at grazing incidence. Typically around 1.0. float F_Schlick(float LoH, float f0, float f90) { float term5 = pow5(1.0f - LoH); return f0 + (f90 - f0) * term5; } float3 F_Schlick(float LoH, float3 f0, float f90) { float term5 = pow5(1.0f - LoH); float3 f90v = float3(f90, f90, f90); return f0 + (f90v - f0) * term5; } // Walter "Microfacet Models for Refraction through Rough Surfaces" // Equation 33. // In the paper: // - m = microsurface normal // - n = macrosurface normal // - theta_m = angle between micro- & macrosurface normals // - alpha = roughness // - cos(theta_m) = NoH // Per sohcahtoa: // tan(theta) = sin(theta) / cos(theta) // tan^2(theta) = sin^2(theta) / cos^2(theta) // = (1 - cos^2(theta)) / cos^2(theta) // = -1 + 1 / cos^2(theta) float D_GGX(float roughness, float NoH) { float r2 = roughness * roughness; float NoH2 = NoH * NoH; float NoH4 = NoH2 * NoH2; float k = rcp(NoH2) - 1; float r2_plus_k = r2 + k; float denom = NoH4 * r2_plus_k * r2_plus_k; return r2 / denom; } // Hammon "PBR Diffuse Lighting for GGX+Smith Microsurfaces" // Slide 84. Note that we remove the (4 * NoL * NoV) from the // denominator of the specular lobe because of some cancellations. // The original, un-optimized equation is: // 2 * NoL * NoV / lerp(2 * NoL * NoV, NoL + NoV, roughness) float G_GGXSmith(float roughness, float NoL, float NoV) { float denom = 2.0f * lerp(2.0f * NoL * NoV, NoL + NoV, roughness); return rcp(denom); } float4 brdf(Pbr pbr, LightData data) { float3 specular = 0; float3 diffuse = 0; float f0 = 0.04f; const float f90 = 1.0f; //#define FURNACE_TEST_DIRECT #if defined(FURNACE_TEST_DIRECT) // Create the conditions for the standard BRDF furnace test. // Only applies to the direct lighting stage. The only variable left over is // NoV. f0 = 1; data.direct.color = 1; data.direct.NoL = 1; data.direct.NoH = 1; data.direct.LoH = 1; #endif float2 dfg_uv = float2(data.common.NoV, pbr.roughness_perceptual); float3 dfg; [branch] if (textureExists(_DFG_LUT)) { dfg = _DFG_LUT.SampleLevel(bilinear_clamp_s, dfg_uv, 0).rgb; } else { dfg = float3(1, 1, 1); } #if defined(_CLEARCOAT) const float cc_f0 = 0.04f; float2 cc_dfg_uv = float2(data.common.NoV_cc, pbr.cc_roughness_perceptual); float3 cc_dfg; [branch] if (textureExists(_DFG_LUT)) { cc_dfg = _DFG_LUT.SampleLevel(bilinear_clamp_s, cc_dfg_uv, 0).rgb; } else { cc_dfg = float3(1, 1, 1); } float cc_Ess = max(cc_dfg.y, 1e-4f); //float cc_energy_comp = 1.0f + cc_f0 * (rcp(cc_Ess) - 1.0f); float cc_energy_comp = 1; #endif float3 f0_color = lerp(f0, pbr.albedo.xyz, pbr.metallic); //float3 energy_comp = 1.0f + f0_color * (rcp(dfg.yyy) - 1.0f); float3 energy_comp = 1.0f; // Direct { float3 remainder = 1.0f; #if defined(_CLEARCOAT) float Fcc = F_Schlick(data.direct.LoH, cc_f0, f90); float Dcc = D_GGX(pbr.cc_roughness, data.direct.NoH_cc); float Gcc = G_GGXSmith(pbr.cc_roughness, data.direct.NoL_cc, data.common.NoV_cc); float DFGcc = Fcc * Dcc * Gcc; float3 direct_specular_cc = DFGcc * data.direct.color * data.direct.NoL_cc * pbr.cc_strength; direct_specular_cc *= cc_energy_comp; direct_specular_cc *= remainder; direct_specular_cc = max(0, direct_specular_cc); specular += direct_specular_cc; remainder *= saturate(1.0f - Fcc * pbr.cc_strength); #endif float3 F = F_Schlick(data.direct.LoH, f0_color, f90); float D = D_GGX(pbr.roughness, data.direct.NoH); float G = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV); float3 direct_specular = (D * G) * F; direct_specular *= data.direct.color * data.direct.NoL; direct_specular *= energy_comp; direct_specular *= remainder; direct_specular = max(0, direct_specular); specular += direct_specular; #if defined(F_OREN_NAYAR) float Fd = Fd_OrenNayar(pbr.roughness, data.common.NoV, data.direct.NoL, data.direct.LoV); #else float Fd = Fd_Lambertian(data.direct.NoL); #endif float3 direct_diffuse = Fd * (1.0f - pbr.metallic) * pbr.albedo.xyz * data.direct.color; direct_diffuse *= remainder; direct_diffuse = max(0, direct_diffuse); diffuse += direct_diffuse; } // Indirect #if !defined(FURNACE_TEST_DIRECT) && (defined(FORWARD_BASE_PASS) || defined(OUTLINES_PASS)) { float3 specular_dfg = dfg.xxx * f0_color + dfg.yyy; // filament 5.3.4.6 float3 indirect_specular = data.indirect.specular * specular_dfg; specular += indirect_specular; float3 indirect_diffuse = pbr.albedo.xyz * data.indirect.diffuse * (1.0 - pbr.metallic); diffuse += indirect_diffuse; } #endif #if defined(FORWARD_BASE_PASS) { [branch] if (_UdonLightVolumeEnabled) { specular += LightVolumeSpecular(pbr.albedo.xyz, pbr.smoothness, pbr.metallic, pbr.normal, data.common.V, data.indirect.L00, data.indirect.L01r, data.indirect.L01g, data.indirect.L01b); } } #endif specular *= data.common.spec_ao; #if defined(_EMISSIONS) && defined(FORWARD_BASE_PASS) float3 emission = pbr.emission; #else float3 emission = 0; #endif return float4(diffuse + specular + emission, 1); } #endif // __BRDF_INC