public override Spectrum f(Vector3 <float> wo, Vector3 <float> wi) { var cosThetaO = AbsCosTheta(wo); var cosThetaI = AbsCosTheta(wi); var wh = wi + wo; // Handle degenerate cases at grazing angles if (cosThetaI == 0 || cosThetaO == 0) { return(Spectrum.Zero); } if (wh.LengthSquared() == 0) { return(Spectrum.Zero); } wh = wh.Normalized(); var f = fresnel.Evaluate(wi.Dot(wh)); // Torrance-Sparrow model return ((reflectance * distribution.D(wh) * distribution.G(wo, wi) * f) / (4 * cosThetaI * cosThetaO)); }
public override Spectrum Sample_f(Vector3 <float> wo, out Vector3 <float> wi, Point2 <float> sample, out float pdf, out BxDFType type) { // Compute the perfect specular reflection direction // // In polar coordinates: // phi_o = phi_i + pi // theta_o = theta_i wi = new Vector3 <float>(-wo.X, -wo.Y, wo.Z); // Simplified because we are in the BRDF coordinate frame pdf = 1; sample = null; type = 0; // TODO? return(fresnel.Evaluate(CosTheta(wi)) * reflectance * (1.0f / AbsCosTheta(wi))); }
public override SampledSpectrum F(Vector3 incoming, Vector3 leaving) { var cosThetaO = AbsCosTheta(ref leaving); var cosThetaI = AbsCosTheta(ref incoming); if (cosThetaI == 0 || cosThetaO == 0) { return(new SampledSpectrum(0)); } var wh = Vector3.Add(incoming, leaving).Normalized(); var cosThetaH = Vector3.Dot(incoming, leaving); var f = _fresnel.Evaluate(cosThetaH); return(_spectrum * _distribution.D(ref wh) * G(ref incoming, ref leaving, ref wh) * f / (4 * cosThetaI * cosThetaO)); }
public RgbColor Evaluate(Vector3 outDir, Vector3 inDir, bool isOnLightSubpath) { if (!ShadingSpace.SameHemisphere(outDir, inDir)) { return(RgbColor.Black); } float cosThetaO = ShadingSpace.AbsCosTheta(outDir); float cosThetaI = ShadingSpace.AbsCosTheta(inDir); Vector3 halfVector = inDir + outDir; // Handle degenerate cases for microfacet reflection if (cosThetaI == 0 || cosThetaO == 0) { return(RgbColor.Black); } if (halfVector.X == 0 && halfVector.Y == 0 && halfVector.Z == 0) { return(RgbColor.Black); } // For the Fresnel call, make sure that wh is in the same hemisphere // as the surface normal, so that total internal reflection is handled correctly. halfVector = Vector3.Normalize(halfVector); if (ShadingSpace.CosTheta(halfVector) < 0) { halfVector = -halfVector; } var cosine = Vector3.Dot(inDir, halfVector); var f = fresnel.Evaluate(cosine); var nd = distribution.NormalDistribution(halfVector); var ms = distribution.MaskingShadowing(outDir, inDir); return(tint * nd * ms * f / (4 * cosThetaI * cosThetaO)); }