Exemplo n.º 1
0
        static void Main( string[] args )
        {
            try
            {
                // Analyze arguments
                if ( args.Length != 3 )
                    throw new Exception( "Usage: BRDFLafortuneFitting \"Path to MERL BRDF\" \"Path to Lafortune Coeffs file\" AmountOfCosineLobes" );

                FileInfo	SourceBRDF = new FileInfo( args[0] );
                if ( !SourceBRDF.Exists )
                    throw new Exception( "Source BRDF file \"" + SourceBRDF.FullName + "\" does not exist!" );
                FileInfo	TargetFile = new FileInfo( args[1] );
                if ( !TargetFile.Directory.Exists )
                    throw new Exception( "Target coefficient file's \"" + TargetFile.FullName + "\" directory does not exist!" );
                int	LobesCount = 0;
                if ( !int.TryParse( args[2], out LobesCount ) )
                    throw new Exception( "3rd argument must be an integer number!" );
                if ( LobesCount <= 0 || LobesCount > 100 )
                    throw new Exception( "Number of lobes must be in [1,100]!" );

                // Attempt to create the target file first, just to ensure we don't bump into a problem after the lengthy minimization process...
                try
                {
                    using ( TargetFile.Create() ) {}
                }
                catch ( Exception _e )
                {
                    throw new Exception( "Failed to create the target coefficients file: " + _e.Message );
                }

                // Load the BRDF
                double[][]	BRDF = LoadBRDF( SourceBRDF );

            // 				// DEBUG CHECK
            // 				{	// Generate a bunch of incoming/outgoing directions, get their Half/Diff angles then regenerate back incoming/outgoing directions from these angles and check the relative incoming/outgoing directions are conserved
            // 					// This is important to ensure we sample only the relevant (i.e. changing) parts of the BRDF in our minimization scheme
            // 					// (I want to actually sample the BRDF using the half/diff angles and generate incoming/outgoing vectors from these, rather than sample all the possible 4D space)
            // 					//
            // 					Random	TempRNG = new Random( 1 );
            // 					Vector3	TempIn = new Vector3(), TempOut = new Vector3();
            // 					double	MinThetaHalf = double.MaxValue, MaxThetaHalf = -double.MaxValue;
            // 					double	MinPhiHalf = double.MaxValue, MaxPhiHalf = -double.MaxValue;
            // 					double	MinThetaDiff = double.MaxValue, MaxThetaDiff = -double.MaxValue;
            // 					double	MinPhiDiff = double.MaxValue, MaxPhiDiff = -double.MaxValue;
            // 					for ( int i=0; i < 10000; i++ )
            // 					{
            // 						double	Phi_i = 2.0 * Math.PI * (TempRNG.NextDouble() - 0.5);
            // 						double	Theta_i = 0.5 * Math.PI * TempRNG.NextDouble();
            // 						double	Phi_r = 2.0 * Math.PI * (TempRNG.NextDouble() - 0.5);
            // 						double	Theta_r = 0.5 * Math.PI * TempRNG.NextDouble();
            //
            // 						double	Theta_half, Phi_half, Theta_diff, Phi_diff;
            // 						std_coords_to_half_diff_coords( Theta_i, Phi_i, Theta_r, Phi_r, out Theta_half, out Phi_half, out Theta_diff, out Phi_diff );
            //
            //
            // // 						MinThetaHalf = Math.Min( MinThetaHalf, Theta_half );
            // // 						MaxThetaHalf = Math.Max( MaxThetaHalf, Theta_half );
            // // 						MinThetaDiff = Math.Min( MinThetaDiff, Theta_diff );
            // // 						MaxThetaDiff = Math.Max( MaxThetaDiff, Theta_diff );
            // // 						MinPhiHalf = Math.Min( MinPhiHalf, Phi_half );
            // // 						MaxPhiHalf = Math.Max( MaxPhiHalf, Phi_half );
            // // 						MinPhiDiff = Math.Min( MinPhiDiff, Phi_diff );
            // // 						MaxPhiDiff = Math.Max( MaxPhiDiff, Phi_diff );
            //
            // // 						if ( Theta_half > MaxThetaHalf )
            // // 						{
            // // 							MaxThetaHalf = Theta_half;
            // // 							MaxPhiHalf = Phi_half;
            // // 							MaxThetaDiff = Theta_diff;
            // // 							MaxPhiDiff = Phi_diff;
            // // 						}
            //
            // 						// Convert back...
            // 						double	NewTheta_i, NewPhi_i, NewTheta_r, NewPhi_r;
            // 						half_diff_coords_to_std_coords( Theta_half, Phi_half, Theta_diff, Phi_diff, out NewTheta_i, out NewPhi_i, out NewTheta_r, out NewPhi_r );
            //
            // 						// Convert back into directions
            // 						half_diff_coords_to_std_coords( Theta_half, Phi_half, Theta_diff, Phi_diff, ref TempIn, ref TempOut );
            //
            // 						// Check
            // 						const double Tol = 1e-4;
            // 						if ( Math.Abs( NewTheta_i - Theta_i ) > Tol
            // 							|| Math.Abs( NewTheta_r - Theta_r ) > Tol )
            // 							throw new Exception( "ARGH THETA!" );
            // 						if ( Math.Abs( NewPhi_i - Phi_i ) > Tol
            // 							|| Math.Abs( NewPhi_r - Phi_r ) > Tol )
            // 							throw new Exception( "ARGH PHI!" );
            //
            // 						if ( NewTheta_i > 0.5 * Math.PI )
            // 							throw new Exception( "Incoming direction below surface!" );
            // 						if ( NewTheta_r > 0.5 * Math.PI )
            // 							throw new Exception( "Outgoing direction below surface!" );
            // 						if ( TempIn.z < 0.0 )
            // 							throw new Exception( "VECTOR Incoming direction below surface!" );
            // 						if ( TempOut.z < 0.0 )
            // 							throw new Exception( "VECTOR Outgoing direction below surface!" );
            // 					}
            // 				}
            // 				// DEBUG CHECK

            // DEBUG CHECK
            // I can't understand the purpose of certain "wrong angles" in the BRDF table
            // When Theta_half is near PI/2, and difference angles make the incoming or outgoing directions go BELOW the f*****g surface, what does it mean????
            // {
            // 	double	ThetaHalf = 0.5*Math.PI;
            // 	double	ThetaDiff = 0.1*Math.PI;	// This should make the incoming direction go BELOW the surface...
            // 	double	PhiDiff = 0.0;
            //
            // 	// Check it's below the surface
            // 	Vector3	TempIn = new Vector3(), TempOut = new Vector3();
            // 	half_diff_coords_to_std_coords( ThetaHalf, 0, ThetaDiff, PhiDiff, ref TempIn, ref TempOut );
            //
            // 	// Get BRDF index
            // 	int	TableIndex = PhiDiff_index( PhiDiff );
            // 		TableIndex += (BRDF_SAMPLING_RES_PHI_D / 2) * ThetaDiff_index( ThetaDiff );
            // 		TableIndex += (BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D / 2) * ThetaHalf_index( ThetaHalf );
            //
            // 	// What the f**k is there??
            // 	double	Value = BRDF[0][TableIndex];
            //
            // 	// A tiny negative value...
            // }
            // DEBUG CHECK

            // DEBUG Clear out BRDF values to make sure we only read positive samples. If we find a negative sample then there is an error!
            // for ( int PhiDiffIndex=0; PhiDiffIndex < BRDF_SAMPLING_RES_PHI_D/2; PhiDiffIndex++ )
            // 	for ( int ThetaDiffIndex=0; ThetaDiffIndex < BRDF_SAMPLING_RES_THETA_D; ThetaDiffIndex++ )
            // 		for ( int ThetaHalfIndex=0; ThetaHalfIndex < BRDF_SAMPLING_RES_THETA_H; ThetaHalfIndex++ )
            // 		{
            // 			int	TableIndex = PhiDiffIndex;
            // 				TableIndex += (BRDF_SAMPLING_RES_PHI_D / 2) * ThetaDiffIndex;
            // 				TableIndex += (BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D / 2) * ThetaHalfIndex;
            //
            // 			BRDF[0][TableIndex] = -1.0f;
            // 		}
            // DEBUG

                // Generate the sampling base
                // => We generate and store as many samples as possible in the 90*90*360/2 source array
                // => We generate the associated incoming/outgoing direction
                // => We store the 3 dot product coefficients for the sample, which we will use to evaluate the cosine lobe
                //
                Random	RNG = new Random( 1 );
                double	dPhi = Math.PI / (2*SAMPLES_COUNT_THETA);
                double	dTheta = 0.5*Math.PI / SAMPLES_COUNT_THETA;

                Vector3	MinValues = new Vector3() { x=+double.MaxValue, y=+double.MaxValue, z=+double.MaxValue };
                Vector3	MaxValues = new Vector3() { x=-double.MaxValue, y=-double.MaxValue, z=-double.MaxValue };

                Vector3	In = new Vector3();
                Vector3	Out = new Vector3();
                List<BRDFSample>	Samples = new List<BRDFSample>();
                for ( int PhiDiffIndex=0; PhiDiffIndex < 2*SAMPLES_COUNT_THETA; PhiDiffIndex++ )
                {
                    for ( int ThetaDiffIndex=0; ThetaDiffIndex < SAMPLES_COUNT_THETA; ThetaDiffIndex++ )
                    {
                        for ( int ThetaHalfIndex=0; ThetaHalfIndex < SAMPLES_COUNT_THETA; ThetaHalfIndex++ )
                        {
                            // Generate random stratified samples
                            double	PhiDiff = dPhi * (PhiDiffIndex + RNG.NextDouble());
                            double	ThetaDiff = dTheta * (ThetaDiffIndex + RNG.NextDouble());
                            double	ThetaHalf = dTheta * (ThetaHalfIndex + RNG.NextDouble());
            // 							double	PhiDiff = dPhi * PhiDiffIndex;
            // 							double	ThetaDiff = dTheta * ThetaDiffIndex;
            // 							double	ThetaHalf = dTheta * ThetaHalfIndex;

                            // Retrieve incoming/outgoing vectors
                            half_diff_coords_to_std_coords( ThetaHalf, 0.0, ThetaDiff, PhiDiff, ref In, ref Out );

                            // Build the general BRDF index
                            int	TableIndex = PhiDiff_index( PhiDiff );
                                TableIndex += (BRDF_SAMPLING_RES_PHI_D / 2) * ThetaDiff_index( ThetaDiff );
                                TableIndex += (BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D / 2) * ThetaHalf_index( ThetaHalf );

                            // Check the in & out directions are valid (i.e. ABOVE the surface)
                            const double	Z_TOL = 0.001;
                            if ( In.z <= Z_TOL || Out.z <= Z_TOL )
                                continue;	// Invalid sample...

                            //////////////////////////////////////////////////////////////////////////
                            // Replace any invalid value
                            double	R = BRDF[0][TableIndex];
                            double	G = BRDF[1][TableIndex];
                            double	B = BRDF[2][TableIndex];

                            double	SumValidValues = 0.0;
                            int		ValidValuesCount = 0;
                            if ( IsValid( R ) )
                            {	// Red is valid
                                SumValidValues += R;
                                ValidValuesCount++;
                            }
                            if ( IsValid( G ) )
                            {	// Green is valid
                                SumValidValues += G;
                                ValidValuesCount++;
                            }
                            if ( IsValid( B ) )
                            {	// Blue is valid
                                SumValidValues += B;
                                ValidValuesCount++;
                            }
                            if ( ValidValuesCount != 3 )
                            {
                                SumValidValues /= Math.Max( 1, ValidValuesCount );	// Get the average of valid values
                                if ( !IsValid( R ) )
                                    R = SumValidValues;	// Replace Red by average...
                                if ( !IsValid( G ) )
                                    G = SumValidValues;	// Replace Green by average...
                                if ( !IsValid( B ) )
                                    B = SumValidValues;	// Replace Blue by average...

                                BRDF[0][TableIndex] = R;
                                BRDF[1][TableIndex] = G;
                                BRDF[2][TableIndex] = B;
                            }

                            //////////////////////////////////////////////////////////////////////////
                            BRDFSample	Sample = new BRDFSample();
                            Samples.Add( Sample );

                            Sample.m_BRDFIndex = TableIndex;

                            // Store the cos(ThetaIn)
                            Sample.m_CosThetaIn = In.z;

                            // Build the dot product coefficients used by the Lafortune model
                            Sample.m_DotProduct.Set(
                                In.x*Out.x,
                                In.y*Out.y,
                                In.z*Out.z
                                );

            // DEBUG Keep min/max values to have an idea of what we're manipulating here...
            MinValues.x = Math.Min( MinValues.x, R );
            MaxValues.x = Math.Max( MaxValues.x, R );
            MinValues.y = Math.Min( MinValues.y, G );
            MaxValues.y = Math.Max( MaxValues.y, G );
            MinValues.z = Math.Min( MinValues.z, B );
            MaxValues.z = Math.Max( MaxValues.z, B );
            // DEBUG

            // DEBUG
            // Patch the BRDF to make it look like an obvious cosine lobe from a standard Phong reflection
            // {
            // 	Vector3	Reflect = new Vector3() { x=-In.x, y=-In.y, z=In.z };
            // 	double	Dot = Reflect.Dot( ref Out );
            //
            // // 	Vector3	Half = new Vector3() { x=In.x+Out.x, y=In.y+Out.y, z=In.z+Out.z };
            // // 			Half.Normalize();
            // // 	double	Dot = Half.z;
            // 			Dot = Math.Max( 0, Dot );
            // 			Dot = Math.Pow( Dot, 17.2 );	// The exponent is very particular
            // //			Dot /= In.z;					// / cos(ThetaIn)
            //
            // 	BRDF[0][TableIndex] = Dot;
            // }
            // DEBUG
                        }
                    }
                }

                // We got our samples!
                ms_BRDFSamples = Samples.ToArray();

                double	PercentageOfTableUsed = (double) ms_BRDFSamples.Length / (BRDF_SAMPLING_RES_THETA_H*BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D / 2);

                // Show modeless progress form
                ms_ProgressForm = new ProgressForm();
                ms_ProgressForm.Show();
                ms_ProgressForm.BRDFComponentIndex = 0;
                ms_ProgressForm.Progress = 0.0;

                // Build a list of initial guesses
                CosineLobe[]	InitialGuesses = new CosineLobe[10];
                InitialGuesses[0] = new CosineLobe(
                    new Vector3() { x=-1, y=-1, z=1 },	// Standard Phong reflection
            // 					new Vector3() { x=0, y=0, z=1 },	// Dumb test
            // 					17.2 );
             					1.0 );

                for ( int i=1; i < InitialGuesses.Length; i++ )
                {
                    Vector3	Direction = new Vector3() { x=2.0*RNG.NextDouble()-1.0, y=2.0*RNG.NextDouble()-1.0, z=RNG.NextDouble() };
                    InitialGuesses[i] = new CosineLobe( Direction, 1.0 );
                }

                // Perform local minimization for each R,G,B component
                CosineLobe[][]	CosineLobes = new CosineLobe[3][];
                double[][]		RMSErrors = new double[3][];
                try
                {
                    for ( int ComponentIndex=0; ComponentIndex < 3; ComponentIndex++ )
                    {
                        CosineLobes[ComponentIndex] = new CosineLobe[LobesCount];
                        RMSErrors[ComponentIndex] = new double[LobesCount];

                        ms_ProgressForm.BRDFComponentIndex = ComponentIndex;

                        FitBRDF( BRDF[ComponentIndex], CosineLobes[ComponentIndex], InitialGuesses, BFGS_CONVERGENCE_TOLERANCE, RMSErrors[ComponentIndex], ShowProgress );

            // 						// CHECK We get the same result starting from another initial guess
            // 						CosineLobe[]	TempLobes = new CosineLobe[LobesCount];
            // 						InitialGuesses[0] = new CosineLobe( new Vector3() { x=0, y=0, z=1 }, 1 );
            // 						FitBRDF( BRDF[ComponentIndex], TempLobes, InitialGuesses, BFGS_CONVERGENCE_TOLERANCE, RMSErrors[ComponentIndex], ShowProgress );
            // 						// CHECK
                    }
                }
                catch ( Exception _e )
                {
                    throw new Exception( "BRDF Fitting failed: " + _e.Message );
                }

                ms_ProgressForm.Dispose();

                // Save the result
                try
                {
                    using ( FileStream Stream = TargetFile.Create() )
                        using ( BinaryWriter Writer = new BinaryWriter( Stream ) )
                        {
                            // Write the amount of lobes
                            Writer.Write( LobesCount );

                            // Write the coefficients
                            for ( int ComponentIndex=0; ComponentIndex < 3; ComponentIndex++ )
                            {
                                for ( int LobeIndex=0; LobeIndex < LobesCount; LobeIndex++ )
                                {
                                    CosineLobe	Lobe = CosineLobes[ComponentIndex][LobeIndex];
                                    Writer.Write( Lobe.C.x );
                                    Writer.Write( Lobe.C.y );
                                    Writer.Write( Lobe.C.z );
                                    Writer.Write( Lobe.N );
                                }
                            }
                        }
                }
                catch ( Exception _e )
                {
                    throw new Exception( "Error writing the result cosine lobe coefficients: " + _e.Message );
                }
            }
            catch ( Exception _e )
            {
                MessageBox.Show( "An error occurred!\r\n\r\n" + _e.Message + "\r\n\r\n" + _e.StackTrace, "BRDF Fitting", MessageBoxButtons.OK, MessageBoxIcon.Warning );
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Computes the square difference between a current cosine lobe estimate and a goal BRDF given a set of samples
        /// </summary>
        /// <param name="_SamplesCollection">The collection of samples to use for the computation</param>
        /// <param name="_Normalizer">The normalizer for the final result</param>
        /// <param name="_GoalBDRF">The goal BRDF function to compute square difference from</param>
        /// <param name="_LobeEstimates">The cosine lobes matching the BRDF</param>
        /// <returns>The square difference between goal and estimate</returns>
        private static double ComputeSummedDifferences( BRDFSample[] _Samples, double _Normalizer, double[] _GoalBDRF, CosineLobe[] _LobeEstimates )
        {
            // Sum differences between current ZH estimates and current SH goal estimates
            double	SumSquareDifference = 0.0;
            double	GoalValue, CurrentValue, TempLobeDot;

            int		SamplesCount = _Samples.Length;
            int		LobesCount = _LobeEstimates.Length;

            for ( int SampleIndex=0; SampleIndex < SamplesCount; SampleIndex++ )
            {
                BRDFSample	Sample = _Samples[SampleIndex];

                // Get BRDF value for that sample
                GoalValue = _GoalBDRF[Sample.m_BRDFIndex];
            // 				if ( GoalValue < 0.0 )
            // 					throw new Exception( "Unexpected negative value!" );
                if ( double.IsNaN( GoalValue ) )
                    throw new Exception( "Unexpected NaN!" );

                // DEBUG => Multiply by cos(ThetaIn) to test if it gets us a better conditioning
             				GoalValue *= Sample.m_CosThetaIn;

                // Estimate cosine lobe value in that direction
                CurrentValue = 0.0;
                for ( int LobeIndex=0; LobeIndex < LobesCount; LobeIndex++ )
                {
                    CosineLobe	Lobe = _LobeEstimates[LobeIndex];
                    TempLobeDot = Lobe.C.x * Sample.m_DotProduct.x + Lobe.C.y * Sample.m_DotProduct.y + Lobe.C.z * Sample.m_DotProduct.z;
                    TempLobeDot = Math.Max( 0.0, TempLobeDot );
                    TempLobeDot = Math.Pow( TempLobeDot, Lobe.N );
                    CurrentValue += TempLobeDot;
                }

                // Sum difference between estimate and goal
                SumSquareDifference += (CurrentValue - GoalValue) * (CurrentValue - GoalValue);
            //				SumSquareDifference += Math.Abs( CurrentValue - GoalValue );	// Just to avoid super large numbers!
            }

            // Normalize
            SumSquareDifference *= _Normalizer;

            return	SumSquareDifference;
        }
Exemplo n.º 3
0
        /// <summary>
        /// Performs mapping of a BRDF into N sets of cosine lobes coefficients
        /// WARNING: Takes hell of a time to compute !
        /// </summary>
        /// <param name="_BRDF">The BRDF to fit into cosine lobes</param>
        /// <param name="_Lobes">The array of resulting cosine lobes</param>
        /// <param name="_InitialGuesses">An array of initial lobe coefficients</param>
        /// <param name="_BFGSConvergenceTolerance">The convergence tolerance for the BFGS algorithm (the lower the tolerance, the longer it will compute)</param>
        /// <param name="_RMS">The resulting array of RMS errors for each cosine lobe</param>
        /// <param name="_Delegate">An optional delegate to pass the method to get feedback about the mapping as it can be a lengthy process (!!)</param>
        /// <returns>The global minimum for all the cosine lobes put together</returns>
        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;
        }
Exemplo n.º 4
0
 public void CopyFrom( CosineLobe _Source )
 {
     C.x = _Source.C.x;
     C.y = _Source.C.y;
     C.z = _Source.C.z;
     N = _Source.N;
     Error = _Source.Error;
 }