// Port of glsl/pathtracing/metal_brdf.glsl (haze/retro omitted, see specular_brdf.wgsl)
fn metal_fresnel(mu: f32) -> vec3<f32> {
let F_nofilm = FresnelF82Tint(mu, base_weight * base_color, specular_color);
if (thin_film_weight > 0.0) {
let eta_fe = mix(thin_film_ior / ambient_ior, thin_film_ior / coat_ior, coat_weight);
let F_film = FresnelThinFilmOverConductor(mu, eta_fe);
return mix(F_nofilm, F_film, thin_film_weight);
}
return F_nofilm;
}
fn metal_brdf_evaluate(winputL: vec3<f32>, woutputL: vec3<f32>) -> BsdfEval {
var out: BsdfEval;
out.f = vec3<f32>(0.0);
out.pdf = PDF_EPSILON;
if (winputL.z < DENOM_TOLERANCE || woutputL.z < DENOM_TOLERANCE) { return out; }
let a = specular_ndf_roughnesses();
let mR = normalize(woutputL + winputL);
let D = ggx_ndf_eval(mR, a.x, a.y);
let DV = D * ggx_G1(winputL, a.x, a.y) * max(0.0, dot(winputL, mR)) / max(DENOM_TOLERANCE, winputL.z);
let dwh_dwo = 1.0 / max(abs(4.0 * dot(winputL, mR)), DENOM_TOLERANCE);
let G2 = ggx_G2(winputL, woutputL, a.x, a.y);
let DG2 = D * G2;
out.pdf = max(PDF_EPSILON, DV * dwh_dwo);
let F = metal_fresnel(abs(dot(winputL, mR)));
out.f = metal_energy_scale(winputL) * min(vec3<f32>(1.0), specular_weight * F) * DG2 / max(4.0 * abs(woutputL.z) * abs(winputL.z), DENOM_TOLERANCE);
return out;
}
fn metal_brdf_sample(winputL: vec3<f32>) -> BsdfSample {
var out: BsdfSample;
out.f = vec3<f32>(0.0);
out.woutputL = vec3<f32>(0.0, 0.0, 1.0);
out.pdf = PDF_EPSILON;
if (winputL.z < DENOM_TOLERANCE) { return out; }
let a = specular_ndf_roughnesses();
let mR = ggx_ndf_sample(winputL, a.x, a.y);
let woutputL = -winputL + 2.0 * dot(winputL, mR) * mR;
out.woutputL = woutputL;
if (winputL.z * woutputL.z < FLT_EPSILON) {
out.pdf = 0.0;
return out;
}
let D = ggx_ndf_eval(mR, a.x, a.y);
let DV = D * ggx_G1(winputL, a.x, a.y) * max(0.0, dot(winputL, mR)) / max(DENOM_TOLERANCE, winputL.z);
let dwh_dwo = 1.0 / max(abs(4.0 * dot(winputL, mR)), DENOM_TOLERANCE);
let G2 = ggx_G2(winputL, woutputL, a.x, a.y);
let DG2 = D * G2;
out.pdf = max(PDF_EPSILON, DV * dwh_dwo);
let F = metal_fresnel(abs(dot(winputL, mR)));
out.f = metal_energy_scale(winputL) * min(vec3<f32>(1.0), specular_weight * F) * DG2 / max(4.0 * abs(woutputL.z) * abs(winputL.z), DENOM_TOLERANCE);
return out;
}
fn metal_brdf_albedo(winputL: vec3<f32>) -> vec3<f32> {
if (winputL.z < DENOM_TOLERANCE) { return vec3<f32>(0.0); }
let num_samples = 4;
var albedo = vec3<f32>(0.0);
for (var n = 0; n < num_samples; n++) {
let s = metal_brdf_sample(winputL);
if (length(s.f) > RADIANCE_EPSILON) {
albedo += s.f * abs(s.woutputL.z) / max(PDF_EPSILON, s.pdf);
}
}
albedo /= f32(num_samples);
return albedo;
}