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)); }
/// <summary> /// The Pdf that is used for importance sampling microfacet normals from this distribution. /// This usually importance samples the portion of normals that are in the hemisphere of the outgoing direction. /// </summary> /// <param name="outDir">The outgoing direction in shading space.</param> /// <param name="inDir">The incoming direction in shading space.</param> /// <returns>The pdf value.</returns> public float Pdf(Vector3 outDir, Vector3 normal) => NormalDistribution(normal) * MaskingShadowing(outDir) * MathF.Abs(Vector3.Dot(outDir, normal)) / ShadingSpace.AbsCosTheta(outDir);