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; }
static void RunForm(IBRDF _BRDF, FileInfo _tableFileName, bool _usePreviousRoughnessForFitting) { FitterForm form = new FitterForm(); form.RenderBRDF = true; // Change this to perform fitting without rendering each result (faster) // Just enter view mode to visualize fitting form.UsePreviousRoughness = _usePreviousRoughnessForFitting; //form.DoFitting = false; //form.Paused = true; //form.ReadOnly = true; // Debug a specific case //form.RoughnessIndex = 19; //form.ThetaIndex = 40; form.UseAdaptiveFit = false; form.SetupBRDF(_BRDF, 64, _tableFileName); Application.Run(form); // ApplicationContext ctxt = new ApplicationContext( form ); // ctxt.ThreadExit += ctxt_ThreadExit; // Application.Run( ctxt ); // ctxt.Dispose(); }
public BRDFGenerator(Type targetType, int tableResolution, int sampleCount, LTCTableParametrization parametrization, string outputDir) { this.type = targetType; this.brdf = (IBRDF)Activator.CreateInstance(targetType); this.shouldGenerate = true; this.tableResolution = tableResolution; this.sampleCount = sampleCount; this.outputDir = outputDir; this.parametrization = parametrization; }
public void Execute(int roughnessIndex) { // Create the fitter NelderMead fitter = new NelderMead(3); IBRDF brdf = LTCAreaLight.GetBRDFInterface(lightingModel); // Compute all the missing LTCData (0 of the first line is already done) for (int thetaIndex = 1; thetaIndex < tableResolution; thetaIndex++) { Fit(roughnessIndex, thetaIndex, fitter, brdf); } }
public void Fit(int roughnessIndex, int thetaIndex, NelderMead fitter, IBRDF brdf) { // Compute the roughness and cosTheta for this sample float roughness, cosTheta; GetRoughnessAndAngle(roughnessIndex, thetaIndex, tableResolution, parametrization, out roughness, out cosTheta); // Compute the matching view vector Vector3 tsView = new Vector3(Mathf.Sqrt(1 - cosTheta * cosTheta), 0, cosTheta); // Compute BRDF's magnitude and average direction LTCData currentLTCData; LTCDataUtilities.Initialize(out currentLTCData); LTCDataUtilities.ComputeAverageTerms(brdf, ref tsView, roughness, sampleCount, ref currentLTCData); // Otherwise use average direction as Z vector int previousLTCDataIndex = (thetaIndex - 1) * tableResolution + roughnessIndex; LTCData previousLTC = ltcData[previousLTCDataIndex]; currentLTCData.m11 = previousLTC.m11; currentLTCData.m22 = previousLTC.m22; currentLTCData.m13 = previousLTC.m13; LTCDataUtilities.Update(ref currentLTCData); // Find best-fit LTC lobe (scale, alphax, alphay) if (currentLTCData.magnitude > 1e-6) { double[] startFit = LTCDataUtilities.GetFittingParms(in currentLTCData); double[] resultFit = new double[startFit.Length]; int localSampleCount = sampleCount; currentLTCData.error = (float)fitter.FindFit(resultFit, startFit, (double)k_FitExploreDelta, (double)k_Tolerance, k_MaxIterations, (double[] parameters) => { LTCDataUtilities.SetFittingParms(ref currentLTCData, parameters, false); return(ComputeError(currentLTCData, brdf, localSampleCount, ref tsView, roughness)); }); currentLTCData.iterationsCount = fitter.m_lastIterationsCount; // Update LTC with final best fitting values LTCDataUtilities.SetFittingParms(ref currentLTCData, resultFit, false); } // Store new valid result int currentLTCDataIndex = thetaIndex * tableResolution + roughnessIndex; ltcData[currentLTCDataIndex] = currentLTCData; }
// 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)); }
public SphereShape(float radius, IBRDF brdf, ITexture texture) { this._radius = radius; this.BRDF = brdf; this.Texture = texture; }
public PlaneShape(IBRDF brdf, ITexture texture) { this.BRDF = brdf; this.Texture = texture; this.SetupCorners(); }