private void CreateToyPathTracerScene()
        {
            // This is the scene used in the "ToyPathTracer" project by Aras Pranckevičius
            // https://github.com/aras-p/ToyPathTracer

            Vector3 lookfrom    = new Vector3(0, 2, 3);
            Vector3 lookat      = new Vector3(0, 0, 0);
            float   distToFocus = 3;
            float   aperture    = 0.1f;

            aperture *= 0.2f;

            _sceneParams.Camera = Camera.Create(
                lookfrom,
                lookat,
                Vector3.UnitY,
                60,
                (float)Width / Height,
                aperture,
                distToFocus);

            _spheres = new[]
            {
                Sphere.Create(new Vector3(0, -100.5f, -1), 100),
                Sphere.Create(new Vector3(2, 0, -1), 0.5f),
                Sphere.Create(new Vector3(0, 0, -1), 0.5f),
                Sphere.Create(new Vector3(-2, 0, -1), 0.5f),
                Sphere.Create(new Vector3(2, 0, 1), 0.5f),
                Sphere.Create(new Vector3(0, 0, 1), 0.5f),
                Sphere.Create(new Vector3(-2, 0, 1), 0.5f),
                Sphere.Create(new Vector3(0.5f, 1, 0.5f), 0.5f),
                Sphere.Create(new Vector3(-1.5f, 1.5f, 0f), 0.3f),
                Sphere.Create(new Vector3(4, 0, -3), 0.5f), Sphere.Create(new Vector3(3, 0, -3), 0.5f), Sphere.Create(new Vector3(2, 0, -3), 0.5f), Sphere.Create(new Vector3(1, 0, -3), 0.5f), Sphere.Create(new Vector3(0, 0, -3), 0.5f), Sphere.Create(new Vector3(-1, 0, -3), 0.5f), Sphere.Create(new Vector3(-2, 0, -3), 0.5f), Sphere.Create(new Vector3(-3, 0, -3), 0.5f), Sphere.Create(new Vector3(-4, 0, -3), 0.5f),
                Sphere.Create(new Vector3(4, 0, -4), 0.5f), Sphere.Create(new Vector3(3, 0, -4), 0.5f), Sphere.Create(new Vector3(2, 0, -4), 0.5f), Sphere.Create(new Vector3(1, 0, -4), 0.5f), Sphere.Create(new Vector3(0, 0, -4), 0.5f), Sphere.Create(new Vector3(-1, 0, -4), 0.5f), Sphere.Create(new Vector3(-2, 0, -4), 0.5f), Sphere.Create(new Vector3(-3, 0, -4), 0.5f), Sphere.Create(new Vector3(-4, 0, -4), 0.5f),
                Sphere.Create(new Vector3(4, 0, -5), 0.5f), Sphere.Create(new Vector3(3, 0, -5), 0.5f), Sphere.Create(new Vector3(2, 0, -5), 0.5f), Sphere.Create(new Vector3(1, 0, -5), 0.5f), Sphere.Create(new Vector3(0, 0, -5), 0.5f), Sphere.Create(new Vector3(-1, 0, -5), 0.5f), Sphere.Create(new Vector3(-2, 0, -5), 0.5f), Sphere.Create(new Vector3(-3, 0, -5), 0.5f), Sphere.Create(new Vector3(-4, 0, -5), 0.5f),
                Sphere.Create(new Vector3(4, 0, -6), 0.5f), Sphere.Create(new Vector3(3, 0, -6), 0.5f), Sphere.Create(new Vector3(2, 0, -6), 0.5f), Sphere.Create(new Vector3(1, 0, -6), 0.5f), Sphere.Create(new Vector3(0, 0, -6), 0.5f), Sphere.Create(new Vector3(-1, 0, -6), 0.5f), Sphere.Create(new Vector3(-2, 0, -6), 0.5f), Sphere.Create(new Vector3(-3, 0, -6), 0.5f), Sphere.Create(new Vector3(-4, 0, -6), 0.5f),
                Sphere.Create(new Vector3(1.5f, 1.5f, -2), 0.3f),
            };

            _materials = new[]
            {
                Material.Lambertian(new Vector3(0.8f, 0.8f, 0.8f)),
                Material.Lambertian(new Vector3(0.8f, 0.4f, 0.4f)),
                Material.Lambertian(new Vector3(0.4f, 0.8f, 0.4f)),
                Material.Metal(new Vector3(0.4f, 0.4f, 0.8f), 0),
                Material.Metal(new Vector3(0.4f, 0.8f, 0.4f), 0),
                Material.Metal(new Vector3(0.4f, 0.8f, 0.4f), 0.2f),
                Material.Metal(new Vector3(0.4f, 0.8f, 0.4f), 0.6f),
                Material.Dielectric(1.5f),
                Material.Lambertian(new Vector3(0.8f, 0.6f, 0.2f)),
                Material.Lambertian(new Vector3(0.1f, 0.1f, 0.1f)), Material.Lambertian(new Vector3(0.2f, 0.2f, 0.2f)), Material.Lambertian(new Vector3(0.3f, 0.3f, 0.3f)), Material.Lambertian(new Vector3(0.4f, 0.4f, 0.4f)), Material.Lambertian(new Vector3(0.5f, 0.5f, 0.5f)), Material.Lambertian(new Vector3(0.6f, 0.6f, 0.6f)), Material.Lambertian(new Vector3(0.7f, 0.7f, 0.7f)), Material.Lambertian(new Vector3(0.8f, 0.8f, 0.8f)), Material.Lambertian(new Vector3(0.9f, 0.9f, 0.9f)),
                Material.Metal(new Vector3(0.1f, 0.1f, 0.1f), 0f), Material.Metal(new Vector3(0.2f, 0.2f, 0.2f), 0f), Material.Metal(new Vector3(0.3f, 0.3f, 0.3f), 0f), Material.Metal(new Vector3(0.4f, 0.4f, 0.4f), 0f), Material.Metal(new Vector3(0.5f, 0.5f, 0.5f), 0f), Material.Metal(new Vector3(0.6f, 0.6f, 0.6f), 0f), Material.Metal(new Vector3(0.7f, 0.7f, 0.7f), 0f), Material.Metal(new Vector3(0.8f, 0.8f, 0.8f), 0f), Material.Metal(new Vector3(0.9f, 0.9f, 0.9f), 0f),
                Material.Metal(new Vector3(0.8f, 0.1f, 0.1f), 0f), Material.Metal(new Vector3(0.8f, 0.5f, 0.1f), 0f), Material.Metal(new Vector3(0.8f, 0.8f, 0.1f), 0f), Material.Metal(new Vector3(0.4f, 0.8f, 0.1f), 0f), Material.Metal(new Vector3(0.1f, 0.8f, 0.1f), 0f), Material.Metal(new Vector3(0.1f, 0.8f, 0.5f), 0f), Material.Metal(new Vector3(0.1f, 0.8f, 0.8f), 0f), Material.Metal(new Vector3(0.1f, 0.1f, 0.8f), 0f), Material.Metal(new Vector3(0.5f, 0.1f, 0.8f), 0f),
                Material.Lambertian(new Vector3(0.8f, 0.1f, 0.1f)), Material.Lambertian(new Vector3(0.8f, 0.5f, 0.1f)), Material.Lambertian(new Vector3(0.8f, 0.8f, 0.1f)), Material.Lambertian(new Vector3(0.4f, 0.8f, 0.1f)), Material.Lambertian(new Vector3(0.1f, 0.8f, 0.1f)), Material.Lambertian(new Vector3(0.1f, 0.8f, 0.5f)), Material.Lambertian(new Vector3(0.1f, 0.8f, 0.8f)), Material.Lambertian(new Vector3(0.1f, 0.1f, 0.8f)), Material.Metal(new Vector3(0.5f, 0.1f, 0.8f), 0f),
                Material.Lambertian(new Vector3(0.1f, 0.2f, 0.5f))
            };
        }
        static Camera ReadCamera(IEnumerable <String> ts)
        {
            var ns = from t in ts
                     let p = OptParse(t)
                             where p.HasValue
                             select p.Value;

            if (ns.Count() < 6)
            {
                throw new ApplicationException("Expected 6 values for a camera.");
            }
            var vs = ns.Take(6).ToArray();

            return(Camera.Create(Vector.Make(vs[0], vs[1], vs[2]), Vector.Make(vs[3], vs[4], vs[5])));
        }
        internal static ((int, int), Scene) ReadScene(String fileName)
        {
            var os = new List <SceneObject>();
            var ls = new List <Light>();
            var c  = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, 0.5, 0));
            var v  = (600, 600);

            foreach (var l in File.ReadAllLines(fileName))
            {
                var ts = l.Split(' ', '\t');
                if (ts.Length <= 0)
                {
                    continue;
                }
                switch (ts[0])
                {
                default:
                    break;

                case "#":
                    continue;

                case "sphere":
                    os.Add(ReadSphere(ts.Skip(1)));
                    break;

                case "light":
                    ls.Add(ReadLight(ts.Skip(1)));
                    break;

                case "camera":
                    c = ReadCamera(ts.Skip(1));
                    break;

                case "view":
                    v = ReadView(ts.Skip(1));
                    break;
                }
            }
            return(v, new Scene()
            {
                Things = os.ToArray(), Lights = ls.ToArray(), Camera = c
            });
        }
        private void CreateBookScene(ref uint state)
        {
            Vector3 camPos      = new Vector3(9.5f, 2f, 2.5f);
            Vector3 lookAt      = new Vector3(3, 0.5f, 0.65f);
            float   distToFocus = (camPos - lookAt).Length();
            float   aperture    = 0.01f;

            _sceneParams.Camera = Camera.Create(
                camPos,
                lookAt,
                Vector3.UnitY,
                25f,
                (float)Width / Height,
                aperture,
                distToFocus);

            List <Sphere>   spheres   = new List <Sphere>();
            List <Material> materials = new List <Material>();

            spheres.Add(Sphere.Create(new Vector3(0, -1000, 0), 1000));
            materials.Add(Material.Lambertian(new Vector3(0.5f, 0.5f, 0.5f)));

            int dimension = 5;

            for (int a = -dimension; a < dimension; a++)
            {
                for (int b = -dimension; b < dimension; b++)
                {
                    float   chooseMaterial = RandUtil.RandomFloat(ref state);
                    Vector3 center         = new Vector3(a + 0.9f * RandUtil.RandomFloat(ref state), 0.15f, b + 0.9f * RandUtil.RandomFloat(ref state));
                    if ((center - new Vector3(4, 0.2f, 0)).Length() > 0.9f)
                    {
                        float randOffset = RandUtil.RandomFloat(ref state) * 0.15f;
                        spheres.Add(Sphere.Create(center + Vector3.UnitY * randOffset, 0.15f + randOffset));
                        if (chooseMaterial < 0.8f)
                        {
                            materials.Add(Material.Lambertian(
                                              new Vector3(
                                                  RandUtil.RandomFloat(ref state) * RandUtil.RandomFloat(ref state),
                                                  RandUtil.RandomFloat(ref state) * RandUtil.RandomFloat(ref state),
                                                  RandUtil.RandomFloat(ref state) * RandUtil.RandomFloat(ref state))));
                        }
                        else if (chooseMaterial < 0.95f)
                        {
                            materials.Add(Material.Metal(
                                              new Vector3(
                                                  0.5f * (1 + RandUtil.RandomFloat(ref state)),
                                                  0.5f * (1 + RandUtil.RandomFloat(ref state)),
                                                  0.5f * (1 + RandUtil.RandomFloat(ref state))),
                                              0.5f * (1 + RandUtil.RandomFloat(ref state))));
                        }
                        else
                        {
                            materials.Add(Material.Dielectric(1.5f));
                        }
                    }

                    Debug.Assert(spheres.Count == materials.Count);
                }
            }

            spheres.Add(Sphere.Create(new Vector3(0, 1, 0), 1));
            materials.Add(Material.Dielectric(1.5f));

            spheres.Add(Sphere.Create(new Vector3(-4, 1, 0), 1));
            materials.Add(Material.Lambertian(new Vector3(0.4f, 0.2f, 0.1f)));

            spheres.Add(Sphere.Create(new Vector3(4, 1, 0), 1));
            materials.Add(Material.Metal(new Vector3(0.7f, 0.6f, 0.5f), 0f));

            _spheres   = spheres.ToArray();
            _materials = materials.ToArray();
        }