Example #1
0
        /// <summary>
        /// Calculate the tristimulus values for the given combination of light source, material and observer.
        /// </summary>
        /// <returns>A float array of size 3 containing X, Y and Z in each cell respectively.</returns>
        public static Vector3 CalculateTristimulusValues(LightSource lightSource, Material material, Observer observer, bool clipInvisible = false)
        {
            // TODO: This may not be the most efficient of all approaches, make sure you change this to be more streamlined.

            // Normalize spectra.
            //
            List<SpectralData> spectraBank = new List<SpectralData>();
            spectraBank.Add(lightSource.SpectralPowerDistribution);
            spectraBank.Add(material.ReflectanceDistribution);
            spectraBank.Add(observer.ResponseSpectra[0]);
            spectraBank.Add(observer.ResponseSpectra[1]);
            spectraBank.Add(observer.ResponseSpectra[2]);

            Utilities.NormalizeSpectra(spectraBank);

            // Calculate the normalizing constant for tristimulus integration
            //
            float K = Utilities.TristimulusNormalizingConstant(lightSource, observer);

            float summation;
            int stepSize = lightSource.SpectralPowerDistribution.StepSize;

            // The wave data we need is nested deep inside objects. So grab it into local arrays
            // for convenience of coding.
            //
            float[] lightSourceData, materialData, observerXData, observerYData, observerZData;

            if (clipInvisible)
            {
                // Find out what indexes correspond to wavelengths between 380 and 780 nm.
                // Since the data is normalized, calculating based on 1 source should be enough.
                int startIndex = (380 - lightSource.SpectralPowerDistribution.LowestWavelength) / lightSource.SpectralPowerDistribution.StepSize;
                int count = (780 - 380) / lightSource.SpectralPowerDistribution.StepSize + 1;

                // Sanity check
                if (startIndex < 0)
                {
                    throw new ArgumentException("wavelength data provided started after 380 nm");
                }

                lightSourceData = lightSource.SpectralPowerDistribution.WaveData.GetRange(startIndex, count).ToArray();
                materialData = material.ReflectanceDistribution.WaveData.GetRange(startIndex, count).ToArray();
                observerXData = observer.ResponseSpectra[0].WaveData.GetRange(startIndex, count).ToArray();
                observerYData = observer.ResponseSpectra[1].WaveData.GetRange(startIndex, count).ToArray();
                observerZData = observer.ResponseSpectra[2].WaveData.GetRange(startIndex, count).ToArray();
            }
            else
            {
                lightSourceData = lightSource.SpectralPowerDistribution.WaveData.ToArray();
                materialData = material.ReflectanceDistribution.WaveData.ToArray();
                observerXData = observer.ResponseSpectra[0].WaveData.ToArray();
                observerYData = observer.ResponseSpectra[1].WaveData.ToArray();
                observerZData = observer.ResponseSpectra[2].WaveData.ToArray();
            }

            // Calculate the L*M product array. This reduces the repeated multiplication operations that would otherwise be
            // required.
            //
            float[] lmProductArray = Utilities.ComputeLMProductArray(lightSourceData, materialData);

            Vector3 tristimulusValues = new Vector3();

            // Calculate X
            //
            summation = Utilities.ComputeSummationTerm(lmProductArray, observerXData);
            tristimulusValues.X = K * summation * (float)stepSize;

            // Calculate Y
            //
            summation = Utilities.ComputeSummationTerm(lmProductArray, observerYData);
            tristimulusValues.Y = K * summation * (float)stepSize;

            // Calculate Z
            //
            summation = Utilities.ComputeSummationTerm(lmProductArray, observerZData);
            tristimulusValues.Z = K * summation * (float)stepSize;

            return tristimulusValues;
        }
Example #2
0
        private static Material RefineMetamer(LightSource referenceLight, Material referenceMaterial, Observer referenceObserver, Material oreMaterial, float nudgeAmount, float marginOfError)
        {
            int xPole = (520 - 380) / 10;
            int yPole = (620 - 380) / 10;
            int zPole = (670 - 380) / 10;

            Vector3 referenceXYZ = CalculateTristimulusValues(referenceLight, referenceMaterial, referenceObserver, true);

            bool refiningComplete = false;
            long j = 0;
            while (!refiningComplete)
            {
                j++;
                // Calculate deltas
                Vector3 xyz = CalculateTristimulusValues(referenceLight, oreMaterial, referenceObserver);
                Vector3 delta = referenceXYZ - xyz;

                // check if good enough
                if (delta.Length() < marginOfError)
                {
                    refiningComplete = true;
                    break;
                }

                for (int i = 0; i < oreMaterial.ReflectanceDistribution.WaveData.Count; i++)
                {
                    float xadjustment = (delta.X * nudgeAmount) / Math.Max(Math.Abs(xPole - i), 1);
                    float yadjustment = (delta.Y * nudgeAmount) / Math.Max(Math.Abs(yPole - i), 1);
                    float zadjustment = (delta.Z * nudgeAmount) / Math.Max(Math.Abs(zPole - i), 1);

                    float newValue = oreMaterial.ReflectanceDistribution.WaveData[i] + xadjustment + yadjustment + zadjustment;

                    oreMaterial.ReflectanceDistribution.WaveData[i] = Math.Max(newValue, 0);
                }
            }

            return oreMaterial;
        }
Example #3
0
        private static Material BruteForceMetamericMaterial(LightSource referenceLight, Material referenceMaterial, Observer referenceObserver, float marginOfError)
        {
            Vector3 referenceXYZ = CalculateTristimulusValues(referenceLight, referenceMaterial, referenceObserver, true);

            // calculate the metameric material
            Material metamericMaterial = new Material();
            bool metamerFound = false;
            int i = 0;
            while(!metamerFound)
            {
                metamericMaterial.Name = "Autogenerated material";
                metamericMaterial.ReflectanceDistribution = SpectralData.CreateRandomSpectrum(380, 780, 10, null);

                Vector3 xyz = CalculateTristimulusValues(referenceLight, metamericMaterial, referenceObserver);

                float deltaX = Math.Abs(referenceXYZ.X - xyz.X);
                float deltaY = Math.Abs(referenceXYZ.Y - xyz.Y);
                float deltaZ = Math.Abs(referenceXYZ.Z - xyz.Z);

                if (deltaX < marginOfError && deltaY < marginOfError && deltaZ < marginOfError)
                {
                    break;
                }
                i++;
            }

            Console.WriteLine("iterations: " + i);

            return metamericMaterial;
        }
Example #4
0
        public static Vector4 GetEquivalentRGB(Material material)
        {
            Vector3 materialXYZ = Utilities.CalculateTristimulusValues(
                TheDataManager.GetLightSourceByPartialName("D65"),
                material,
                TheDataManager.GetObserverByPartialName("1964"),
                true);

            return Utilities.CalculateRGBfromXYZ(materialXYZ);
        }
Example #5
0
        public static Material CreateMetamericMaterialSmart(LightSource referenceLight, Material referenceMaterial, Observer referenceObserver)
        {
            Stopwatch stopwatch = new Stopwatch();

            // Start measuring time
            stopwatch.Start();

            // 1: Calculate the reference XYZ values.
            Vector3 referenceXYZ = CalculateTristimulusValues(referenceLight, referenceMaterial, referenceObserver);

            // 2: Calculate a rough metameric material using pure brute force.
            Material metamericMaterial = BruteForceMetamericMaterial(referenceLight, referenceMaterial, referenceObserver, 10.0f);

            //// 3: Now we refine the material so that the metameric match is good.
            //metamericMaterial = RefineMetamer(referenceLight, referenceMaterial, referenceObserver,
            //                                    metamericMaterial, 0.001f, 3);

            //// 4: Now we refine the material so that the metameric match is very good.
            //metamericMaterial = RefineMetamer(referenceLight, referenceMaterial, referenceObserver,
            //                                    metamericMaterial, 0.001f, 2);

            // 5: Now we refine the material so that the metameric match is very good.
            //metamericMaterial = RefineMetamer(referenceLight, referenceMaterial, referenceObserver,
            //                                    metamericMaterial, 0.001f, 3);
            // Stop measuring time
            stopwatch.Stop();

            Console.WriteLine("Time spent was " + stopwatch.Elapsed.Milliseconds + " milliseconds");
            Console.WriteLine("Metamer found!");
            Console.WriteLine(metamericMaterial.Name);

            foreach (float sample in metamericMaterial.ReflectanceDistribution.WaveData)
            {
                Console.WriteLine("value: " + sample);
            }

            return metamericMaterial;
        }
Example #6
0
        /// <summary>
        /// Read in all the materials from the given file.
        /// </summary>
        /// <param name="fileName">Path to the data file.</param>
        protected void ReadInMaterials(string fileName)
        {
            XmlDocument materialsXML = new XmlDocument();
            materialsXML.Load(fileName);

            foreach (XmlElement materialXML in materialsXML.GetElementsByTagName(XMLDataConstants.Material))
            {
                Material material = new Material();
                material.Initialize(materialXML);
                this.Materials.Add(material);
            }
        }