const int SAMPLES_COUNT = 50; // Number of samples used to compute the average terms // compute the average direction of the BRDF public void ComputeAverageTerms(IBRDF _BRDF, ref float3 _tsView, float _alpha) { magnitude = 0.0; fresnel = 0.0; Z = float3.Zero; error = 0.0; double weight, pdf, eval; float3 tsLight = float3.Zero; float3 H = float3.Zero; for (int j = 0; j < SAMPLES_COUNT; ++j) { for (int i = 0; i < SAMPLES_COUNT; ++i) { float U1 = (i + 0.5f) / SAMPLES_COUNT; float U2 = (j + 0.5f) / SAMPLES_COUNT; // sample _BRDF.GetSamplingDirection(ref _tsView, _alpha, U1, U2, ref tsLight); // eval eval = _BRDF.Eval(ref _tsView, ref tsLight, _alpha, out pdf); if (pdf == 0.0f) { continue; } H = (_tsView + tsLight).Normalized; // accumulate weight = eval / pdf; if (double.IsNaN(weight)) { throw new Exception("NaN!"); } magnitude += weight; fresnel += weight * Math.Pow(1 - Math.Max(0.0f, _tsView.Dot(H)), 5.0); Z += (float)weight * tsLight; } } magnitude /= SAMPLES_COUNT * SAMPLES_COUNT; fresnel /= SAMPLES_COUNT * SAMPLES_COUNT; // Finish building the average TBN orthogonal basis Z.y = 0.0f; // clear y component, which should be zero with isotropic BRDFs float length = Z.Length; if (length > 0.0f) { Z /= length; } else { Z = float3.UnitZ; } X.Set(Z.z, 0, -Z.x); Y = float3.UnitY; }
// Compute the error between the BRDF and the LTC using Multiple Importance Sampling static float ComputeError(LTCData ltcData, IBRDF brdf, int sampleCount, ref Vector3 _tsView, float _alpha) { Vector3 tsLight = Vector3.zero; double pdf_BRDF, eval_BRDF; double pdf_LTC, eval_LTC; float sumError = 0.0f; for (int j = 0; j < sampleCount; ++j) { for (int i = 0; i < sampleCount; ++i) { float U1 = (i + 0.5f) / sampleCount; float U2 = (j + 0.5f) / sampleCount; // importance sample LTC { // sample LTCDataUtilities.GetSamplingDirection(ltcData, U1, U2, ref tsLight); eval_BRDF = brdf.Eval(ref _tsView, ref tsLight, _alpha, out pdf_BRDF); eval_LTC = (float)LTCDataUtilities.Eval(ltcData, ref tsLight); pdf_LTC = eval_LTC / ltcData.magnitude; // error with MIS weight float error = Mathf.Abs((float)(eval_BRDF - eval_LTC)); error = error * error * error; // Use L3 norm to favor large values over smaller ones if (error != 0.0f) { error /= (float)pdf_LTC + (float)pdf_BRDF; } if (double.IsNaN(error)) { // SHOULD NEVER HAPPEN } sumError += error; } // importance sample BRDF { // sample brdf.GetSamplingDirection(ref _tsView, _alpha, U1, U2, ref tsLight); // error with MIS weight eval_BRDF = brdf.Eval(ref _tsView, ref tsLight, _alpha, out pdf_BRDF); eval_LTC = LTCDataUtilities.Eval(ltcData, ref tsLight); pdf_LTC = eval_LTC / ltcData.magnitude; float error = Mathf.Abs((float)(eval_BRDF - eval_LTC)); error = error * error * error; // Use L3 norm to favor large values over smaller ones if (error != 0.0f) { error /= (float)pdf_LTC + (float)pdf_BRDF; } if (double.IsNaN(error)) { // SHOULD NEVER HAPPEN } sumError += error; } } } return(sumError / ((float)sampleCount * sampleCount)); }