float ComputeOneDir(Vector3 outDir, Vector3 inDir) { // Compute the half vector float eta = ShadingSpace.CosTheta(outDir) > 0 ? (insideIOR / outsideIOR) : (outsideIOR / insideIOR); Vector3 wh = outDir + inDir * eta; if (wh == Vector3.Zero) { return(0); // Prevent NaN if outDir and inDir exactly align } wh = Vector3.Normalize(wh); if (Vector3.Dot(outDir, wh) * Vector3.Dot(inDir, wh) > 0) { return(0); } // Compute change of variables _dwh\_dinDir_ for microfacet transmission float sqrtDenom = Vector3.Dot(outDir, wh) + eta * Vector3.Dot(inDir, wh); if (sqrtDenom == 0) { return(0); // Prevent NaN in corner case } float dwh_dinDir = Math.Abs((eta * eta * Vector3.Dot(inDir, wh)) / (sqrtDenom * sqrtDenom)); float result = distribution.Pdf(outDir, wh) * dwh_dinDir; Debug.Assert(float.IsFinite(result)); return(result); }
/// <summary> /// Warps the given primary sample to follow the pdf computed by <see cref="Pdf(Vector3, Vector3)"/>. /// </summary> /// <returns>The direction that corresponds to the given primary sample.</returns> public Vector3 Sample(Vector3 outDir, Vector2 primary) { bool flip = ShadingSpace.CosTheta(outDir) < 0; var wh = TrowbridgeReitzSample(flip ? -outDir : outDir, AlphaX, AlphaY, primary.X, primary.Y); if (flip) { wh = -wh; } return(wh); }
public Vector3?Sample(Vector3 outDir, bool isOnLightSubpath, Vector2 primarySample) { if (outDir.Z == 0) { return(null); } Vector3 wh = distribution.Sample(outDir, primarySample); if (Vector3.Dot(outDir, wh) < 0) { return(null); } float eta = ShadingSpace.CosTheta(outDir) > 0 ? (outsideIOR / insideIOR) : (insideIOR / outsideIOR); var inDir = ShadingSpace.Refract(outDir, wh, eta); return(inDir); }
public RgbColor Evaluate(Vector3 outDir, Vector3 inDir, bool isOnLightSubpath) { if (ShadingSpace.SameHemisphere(outDir, inDir)) { return(RgbColor.Black); // transmission only } float cosThetaO = ShadingSpace.CosTheta(outDir); float cosThetaI = ShadingSpace.CosTheta(inDir); if (cosThetaI == 0 || cosThetaO == 0) { return(RgbColor.Black); } // Compute the half vector float eta = ShadingSpace.CosTheta(outDir) > 0 ? (insideIOR / outsideIOR) : (outsideIOR / insideIOR); Vector3 wh = Vector3.Normalize(outDir + inDir * eta); if (ShadingSpace.CosTheta(wh) < 0) { wh = -wh; } if (Vector3.Dot(outDir, wh) * Vector3.Dot(inDir, wh) > 0) { return(RgbColor.Black); } var F = new RgbColor(FresnelDielectric.Evaluate(Vector3.Dot(outDir, wh), outsideIOR, insideIOR)); float sqrtDenom = Vector3.Dot(outDir, wh) + eta * Vector3.Dot(inDir, wh); float factor = isOnLightSubpath ? (1 / eta) : 1; var numerator = distribution.NormalDistribution(wh) * distribution.MaskingShadowing(outDir, inDir); numerator *= eta * eta * Math.Abs(Vector3.Dot(inDir, wh)) * Math.Abs(Vector3.Dot(outDir, wh)); numerator *= factor * factor; var denom = (cosThetaI * cosThetaO * sqrtDenom * sqrtDenom); Debug.Assert(float.IsFinite(denom)); return((RgbColor.White - F) * transmittance * Math.Abs(numerator / denom)); }
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)); }
static Vector3 TrowbridgeReitzSample(Vector3 wi, float alpha_x, float alpha_y, float U1, float U2) { // 1. stretch wi Vector3 wiStretched = Vector3.Normalize(new Vector3(alpha_x * wi.X, alpha_y * wi.Y, wi.Z)); // 2. simulate P22_{wi}(x_slope, y_slope, 1, 1) float slope_x, slope_y; TrowbridgeReitzSample11(ShadingSpace.CosTheta(wiStretched), U1, U2, out slope_x, out slope_y); // 3. rotate float tmp = ShadingSpace.CosPhi(wiStretched) * slope_x - ShadingSpace.SinPhi(wiStretched) * slope_y; slope_y = ShadingSpace.SinPhi(wiStretched) * slope_x + ShadingSpace.CosPhi(wiStretched) * slope_y; slope_x = tmp; // 4. unstretch slope_x = alpha_x * slope_x; slope_y = alpha_y * slope_y; // 5. compute normal return(Vector3.Normalize(new Vector3(-slope_x, -slope_y, 1))); }