static public void Initialize(out LTCData ltcData) { ltcData.magnitude = 1; ltcData.fresnel = 1; ltcData.X.x = 1; ltcData.X.y = 0; ltcData.X.z = 0; ltcData.Y.x = 0; ltcData.Y.y = 1; ltcData.Y.z = 0; ltcData.Z.x = 0; ltcData.Z.y = 0; ltcData.Z.z = 1; ltcData.m11 = 1; ltcData.m22 = 1; ltcData.m13 = 0; ltcData.error = 0; ltcData.iterationsCount = 0; ltcData.detM = 0; MatrixUtilities.Initialize(out ltcData.M); MatrixUtilities.Initialize(out ltcData.invM); }
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)); }
static public void FitInitial(BRDFGenerator brdfGenerator, NelderMead fitter, NativeArray <LTCData> ltcData, int roughnessIndex, int thetaIndex) { // Compute the roughness and cosTheta for this sample float roughness, cosTheta; GetRoughnessAndAngle(roughnessIndex, thetaIndex, brdfGenerator.tableResolution, brdfGenerator.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(brdfGenerator.brdf, ref tsView, roughness, brdfGenerator.sampleCount, ref currentLTCData); // if theta == 0 the lobe is rotationally symmetric and aligned with Z = (0 0 1) currentLTCData.X.x = 1; currentLTCData.X.y = 0; currentLTCData.X.z = 0; currentLTCData.Y.x = 0; currentLTCData.Y.y = 1; currentLTCData.Y.z = 0; currentLTCData.Z.x = 0; currentLTCData.Z.y = 0; currentLTCData.Z.z = 1; if (roughnessIndex == (brdfGenerator.tableResolution - 1)) { // roughness = 1 or no available result currentLTCData.m11 = 1.0f; currentLTCData.m22 = 1.0f; } else { // init with roughness of previous fit LTCData previousLTC = ltcData[roughnessIndex + 1]; currentLTCData.m11 = previousLTC.m11; currentLTCData.m22 = previousLTC.m22; } currentLTCData.m13 = 0; 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]; currentLTCData.error = (float)fitter.FindFit(resultFit, startFit, k_FitExploreDelta, k_Tolerance, k_MaxIterations, (double[] parameters) => { LTCDataUtilities.SetFittingParms(ref currentLTCData, parameters, true); return(ComputeError(currentLTCData, brdfGenerator.brdf, brdfGenerator.sampleCount, ref tsView, roughness)); }); currentLTCData.iterationsCount = fitter.m_lastIterationsCount; // Update LTC with final best fitting values LTCDataUtilities.SetFittingParms(ref currentLTCData, resultFit, true); } // Store new valid result ltcData[roughnessIndex] = currentLTCData; }