public WavelengthCluster Mul(WavelengthCluster c)
        {
            if ((Math.Abs(c.LambdaStart - LambdaStart) > MathLab.Epsilon) || (c.Values.Length != Values.Length))
            {
                return this;
            }

            for (int i = 0; i < Values.Length; i++)
            {
                Values[i] = Values[i] * c.Values[i];
            }

            return this;
        }
 public override void RenderPass()
 {
     var sb = film.GetFreeSampleBuffer();
     var sw = Stopwatch.StartNew();
     var samplesPerPass = Scene.w * Scene.h;
     float dLambda = (SampledSpectrum.sampledLambdaEnd - SampledSpectrum.sampledLambdaStart) / ((float)MaxSpectralSamples + 1f);
     for (int y = 0; y < Scene.h; ++y)
     {
         int pj = 0;
         for (int x = 0; x < Scene.w; ++x, ++pj)
         {
             var cluster = new WavelengthCluster(SampledSpectrum.sampledLambdaStart, dLambda, MaxSpectralSamples);
             //var c = new WlCluster(MaxSpectralSamples);
             //for (int l = 0; l < MaxSpectralSamples / SamplesPerCluster; l++)
             {
                 float[] lambdas = Enumerable.Range(0, MaxSpectralSamples).Select(i => SampleWavelength(rnd.NextFloat())).OrderBy(i=>i).ToArray();
                 RayData cameraRay;
                 Scene.GenerateCameraRay(x, y, out cameraRay);
                 cluster = EvalRadiance(ref cameraRay, cluster, 0);
                 totalSamples += MaxSpectralSamples;
             }
             //cluster.Sort();
             IrregularSPD spd = new IrregularSPD(cluster.Lambdas, cluster.Values, MaxSpectralSamples, dLambda);
             //RegularSPD spd = new RegularSPD(vals, SampledSpectrum.sampledLambdaStart, SampledSpectrum.sampledLambdaEnd, MaxSpectralSamples);
             var pixV = spd.ToRgb();
             sb.SplatSample(x, (Scene.h - y - 1), ref pixV);
             if (sb.IsFull())
             {
                 film.SplatSampleBuffer(true, sb);
                 sb = film.GetFreeSampleBuffer();
             }
         }
     }
     pass++;
     sw.Stop();
     stats = string.Format("MSamples per sec {1:F5}; Total Samples {0}; Samples per pixel {2}; {3}", totalSamples, 0.000001 * (samplesPerPass / sw.Elapsed.TotalSeconds), pass, film.GetStats());
 }
        private WavelengthCluster EvalRadiance(ref RayData r, WavelengthCluster lambdas, int depth)
        {
            double t = 0; // distance to intersection 
            int id = 0; // id of intersected object 
            if (!Scene.Intersect(ref r, ref t, ref id) || depth > MaxDepth)
            {
                return black;
            }
            var obj = SceneManager.Sph[id];
            var x = r.Point((float)t);
            Normal n = (Normal)(x - obj.p).Normalize();
            var nl = Vector.Dot(ref n, ref r.Dir) < 0 ? n : n * -1;
            RayData newRay = new RayData();
            float[] fs = new float[MaxSpectralSamples];
            depth++;
            for (int i = 0; i < MaxSpectralSamples; i++)
            {
                var lambda = lambdas.Lambdas[i];
                var f = obj.c.Sample(lambda);
                var e = 0f;
                var p = f;
                if (obj.refl == 3)
                {
                    e += f;
                }
                if (depth > MaxDepth)
                {
                    if (rnd.NextFloat() < p)
                    {
                        f = f * (1f / p);
                    }
                    else
                    {
                        fs[i] = e;
                    }
                }

                var brdf = Brdfs[obj.refl];
                Vector dir;
                float fb;
                brdf.Sample(ref r.Dir, ref n, rnd.NextFloat(), rnd.NextFloat(), lambda, out dir, out fb);
                newRay = new RayData(ref x, ref dir);
                fs[i] = e + f * fb;
            }
            lambdas.Add(new WavelengthCluster(lambdas.Lambdas, fs));
            return lambdas.Mul(EvalRadiance(ref newRay, lambdas, depth));
        }