BRDFLafortuneFitting.Program.FitBRDF C# (CSharp) Метод

FitBRDF() приватный статический Метод

Performs mapping of a BRDF into N sets of cosine lobes coefficients WARNING: Takes hell of a time to compute !
private static FitBRDF ( double _BRDF, CosineLobe _Lobes, CosineLobe _InitialGuesses, double _BFGSConvergenceTolerance, double _RMS, BRDFMappingFeedback _Delegate ) : double
_BRDF double The BRDF to fit into cosine lobes
_Lobes CosineLobe The array of resulting cosine lobes
_InitialGuesses CosineLobe An array of initial lobe coefficients
_BFGSConvergenceTolerance double The convergence tolerance for the BFGS algorithm (the lower the tolerance, the longer it will compute)
_RMS double The resulting array of RMS errors for each cosine lobe
_Delegate BRDFMappingFeedback An optional delegate to pass the method to get feedback about the mapping as it can be a lengthy process (!!)
Результат double
        private static double FitBRDF( double[] _BRDF, CosineLobe[] _Lobes, CosineLobe[] _InitialGuesses, double _BFGSConvergenceTolerance, double[] _RMS, BRDFMappingFeedback _Delegate )
        {
            int			LobesCount = _Lobes.GetLength( 0 );

            // Build the local function evaluation context
            BRDFFitEvaluationContext	Context = new BRDFFitEvaluationContext();
            Context.m_Lobes = new CosineLobe[1] { new CosineLobe() };
            Context.m_BRDF = new double[_BRDF.Length];
            _BRDF.CopyTo( Context.m_BRDF, 0 );	// Duplicate BRDF as we will modify it for each new lobe

            // Prepare feedback data
            float	fCurrentProgress = 0.0f;
            float	fProgressDelta = 1.0f / (LobesCount * _InitialGuesses.Length);
            int		FeedbackCount = 0;
            int		FeedbackThreshold = (LobesCount * _InitialGuesses.Length) / 100;	// Notify every percent

            //////////////////////////////////////////////////////////////////////////
            // 1] Compute the best fit for each lobe
            int			CrashesCount = 0;
            double[]	LocalLobeCoefficients = new double[1+4];	// Don't forget the BFGS function annoyingly uses indices starting from 1!
            List<CosineLobe>	BestFits = new List<CosineLobe>( _InitialGuesses.Length );
            for ( int LobeIndex=0; LobeIndex < LobesCount; LobeIndex++ )
            {
                BestFits.Clear();

                // 1.1] Perform minification using several attempts with different initial coefficients and keep the best fit
                double	MinError = double.MaxValue;
                for ( int AttemptIndex = 0; AttemptIndex < _InitialGuesses.Length; AttemptIndex++ )
                {
                    // Update external feedback on progression
                    if ( _Delegate != null )
                    {
                        fCurrentProgress += fProgressDelta;
                        FeedbackCount++;
                        if ( FeedbackCount > FeedbackThreshold )
                        {	// Send feedback
                            FeedbackCount = 0;
                            _Delegate( fCurrentProgress );
                        }
                    }

                    // 1.1.1] Set the initial lobe coefficients
                    Context.m_Lobes[0].CopyFrom( _InitialGuesses[AttemptIndex] );

                    // 1.1.2] Copy coefficients into working array
                    LocalLobeCoefficients[1+0] = Context.m_Lobes[0].C.x;
                    LocalLobeCoefficients[1+1] = Context.m_Lobes[0].C.y;
                    LocalLobeCoefficients[1+2] = Context.m_Lobes[0].C.z;
                    LocalLobeCoefficients[1+3] = Context.m_Lobes[0].N;

                    //////////////////////////////////////////////////////////////////////////
                    // At this point, we have a fixed direction and the best estimated ZH coefficients to map the provided SH in this direction.
                    //
                    // We then need to apply BFGS minimization to optimize the ZH coefficients yielding the smallest possible error...
                    //

                    // 1.1.3] Apply BFGS minimization
                    int		IterationsCount = 0;
                    double	FunctionMinimum = 0;
                    try
                    {
                        FunctionMinimum = dfpmin( LocalLobeCoefficients, _BFGSConvergenceTolerance, out IterationsCount, new BFGSFunctionEval( BRDFMappingLocalFunctionEval ), new BFGSFunctionGradientEval( BRDFMappingLocalFunctionGradientEval ), Context );
                    }
                    catch ( Exception )
                    {
                        CrashesCount++;
                        continue;
                    }

                    if ( FunctionMinimum >= MinError )
                        continue;	// Larger error than best candidate so far...

                    MinError = FunctionMinimum;

                    // Save that "optimal" lobe data
                    _Lobes[LobeIndex].C.Set( Context.m_Lobes[0].C.x, Context.m_Lobes[0].C.y, Context.m_Lobes[0].C.z );
                    _Lobes[LobeIndex].N = Context.m_Lobes[0].N;
                    _Lobes[LobeIndex].Error = FunctionMinimum;

                    // Keep in the list of best fits
                    BestFits.Insert( 0, _Lobes[LobeIndex] );

                    _RMS[LobeIndex] = FunctionMinimum;
                }

                //////////////////////////////////////////////////////////////////////////
                // 1.2] At this point, we have the "best" cosine lobe fit for the given BRDF
                // We must subtract the influence of that lobe from the current BRDF and restart fitting with a new lobe...
                //
                double		OldMaxValue = -double.MaxValue;
                double		NewMaxValue = -double.MaxValue;
                CosineLobe	LobeToSubtract = _Lobes[LobeIndex];
                for ( int SampleIndex=0; SampleIndex < ms_BRDFSamples.Length; SampleIndex++ )
                {
                    BRDFSample	Sample = ms_BRDFSamples[SampleIndex];

                    double		LobeInfluence = Sample.m_DotProduct.x*LobeToSubtract.C.x + Sample.m_DotProduct.y*LobeToSubtract.C.y + Sample.m_DotProduct.z*LobeToSubtract.C.z;
                                LobeInfluence = Math.Max( 0.0, LobeInfluence );
                                LobeInfluence = Math.Pow( LobeInfluence, LobeToSubtract.N );

                    double		CurrentBRDFValue = Context.m_BRDF[Sample.m_BRDFIndex];
                                CurrentBRDFValue *= Sample.m_CosThetaIn;

            // 					if ( CurrentBRDFValue > 100.0 )
            // 						return 1;

                    OldMaxValue = Math.Max( OldMaxValue, CurrentBRDFValue );

                                CurrentBRDFValue -= LobeInfluence;
                                CurrentBRDFValue = Math.Max( 0, CurrentBRDFValue );	// Constrain to positive values only

                    NewMaxValue = Math.Max( NewMaxValue, CurrentBRDFValue );

                    Context.m_BRDF[Sample.m_BRDFIndex] = CurrentBRDFValue;
                }
            }

            //////////////////////////////////////////////////////////////////////////
            // 2] At this point, we have a set of SH lobes that are individual best fits to the goal SH coefficients
            // We will finally apply a global BFGS minimzation using all of the total cosine lobes
            //
            double[]	GlobalLobeCoefficients = new double[1+4*_Lobes.Length];	// Don't forget the BFGS function annoyingly uses indices starting from 1!
            ms_TempCoefficientsGlobal = new double[1+4*_Lobes.Length];

            // 2.1] Re-assign the original BRDF to which we compare to
            Context.m_BRDF = _BRDF;

            // 2.2] Re-assign the best lobes as initial best guess
            Context.m_Lobes = _Lobes;
            for ( int LobeIndex=0; LobeIndex < _Lobes.Length; LobeIndex++ )
            {
                CosineLobe	SourceLobe = _Lobes[LobeIndex];
                GlobalLobeCoefficients[1+4*LobeIndex+0] = SourceLobe.C.x;
                GlobalLobeCoefficients[1+4*LobeIndex+1] = SourceLobe.C.y;
                GlobalLobeCoefficients[1+4*LobeIndex+2] = SourceLobe.C.z;
                GlobalLobeCoefficients[1+4*LobeIndex+3] = SourceLobe.N;
            }

            // 2.3] Apply BFGS minimzation to the entire set of coefficients
            int		IterationsCountGlobal = 0;
            double	FunctionMinimumGlobal = double.MaxValue;
            try
            {
                FunctionMinimumGlobal = dfpmin( GlobalLobeCoefficients, _BFGSConvergenceTolerance, out IterationsCountGlobal, new BFGSFunctionEval( BRDFMappingGlobalFunctionEval ), new BFGSFunctionGradientEval( BRDFMappingGlobalFunctionGradientEval ), Context );
            }
            catch ( Exception )
            {
                CrashesCount++;
            }

            // 2.4] Save the final optimized results
            for ( int LobeIndex=0; LobeIndex < _Lobes.Length; LobeIndex++ )
            {
                CosineLobe	TargetLobe = _Lobes[LobeIndex];
                TargetLobe.C.Set( GlobalLobeCoefficients[1+4*LobeIndex+0], GlobalLobeCoefficients[1+4*LobeIndex+1], GlobalLobeCoefficients[1+4*LobeIndex+2] );
                TargetLobe.N = GlobalLobeCoefficients[1+4*LobeIndex+3];
            }

            // Give final 100% feedback
            if ( _Delegate != null )
                _Delegate( 1.0f );

            return FunctionMinimumGlobal;
        }