private Color GetNaturalColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene)
        {
            Color ret = Color.Make(0, 0, 0);

            foreach (Light light in scene.Lights)
            {
                Vector ldis      = Vector.Minus(light.Pos, pos);
                Vector livec     = Vector.Norm(ldis);
                double neatIsect = TestRay(new Ray()
                {
                    Start = pos, Dir = livec
                }, scene);
                bool isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0));
                if (!isInShadow)
                {
                    double illum    = Vector.Dot(livec, norm);
                    Color  lcolor   = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0);
                    double specular = Vector.Dot(livec, Vector.Norm(rd));
                    Color  scolor   = specular > 0 ? Color.Times(Math.Pow(specular, thing.Surface.Roughness), light.Color) : Color.Make(0, 0, 0);
                    ret = Color.Plus(ret, Color.Plus(Color.Times(thing.Surface.Diffuse(pos), lcolor),
                                                     Color.Times(thing.Surface.Specular(pos), scolor)));
                }
            }
            return(ret);
        }
        static Sphere ReadSphere(IEnumerable <String> ts)
        {
            var ns = from t in ts
                     let p = OptParse(t)
                             where p.HasValue
                             select p.Value;

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

            return(new Sphere()
            {
                Center = Vector.Make(vs[0], vs[1], vs[2]),
                Radius = vs[3],
                Surface = new Surface()
                {
                    Diffuse = pos => Color.Make(vs[4], vs[5], vs[6]),
                    Specular = pos => Color.Make(.5, .5, .5),
                    Reflect = pos => vs[7],
                    Roughness = 200 * vs[8]
                }
            });
        }
        private Color Shade(ISect isect, Scene scene, int depth)
        {
            var   d          = isect.Ray.Dir;
            var   pos        = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start);
            var   normal     = isect.Thing.Normal(pos);
            var   reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal));
            Color ret        = Color.DefaultColor;

            ret = Color.Plus(ret, GetNaturalColor(isect.Thing, pos, normal, reflectDir, scene));
            if (depth >= MaxDepth)
            {
                return(Color.Plus(ret, Color.Make(.5, .5, .5)));
            }
            return(Color.Plus(ret, GetReflectionColor(isect.Thing, Vector.Plus(pos, Vector.Times(.001, reflectDir)), normal, reflectDir, scene, depth)));
        }
        static Light ReadLight(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 light.");
            }
            var vs = ns.Take(6).ToArray();
            var n  = vs.Skip(3).Max();

            if (n < 1.0)
            {
                n = 1.0;
            }
            return(new Light()
            {
                Pos = Vector.Make(vs[0], vs[1], vs[2]),
                Color = Color.Make(vs[3] / n, vs[4] / n, vs[5] / n)
            });
        }
Example #5
0
        internal void Render(Scene scene)
        {
            var pixelsQuery =
                from y in Enumerable.Range(0, screenHeight)
                let recenterY = -(y - (screenHeight / 2.0)) / (2.0 * screenHeight)
                                select from x in Enumerable.Range(0, screenWidth)
                                let recenterX                         = (x - (screenWidth / 2.0)) / (2.0 * screenWidth)
                                                            let point =
                    Vector.Norm(Vector.Plus(scene.Camera.Forward,
                                            Vector.Plus(Vector.Times(recenterX, scene.Camera.Right),
                                                        Vector.Times(recenterY, scene.Camera.Up))))
                    let ray = new Ray()
            {
                Start = scene.Camera.Pos, Dir = point
            }
            let computeTraceRay = (Func <Func <TraceRayArgs, Color>, Func <TraceRayArgs, Color> >)
                                      (f => traceRayArgs =>
                                      (from isect in
                                       from thing in traceRayArgs.Scene.Things
                                       select thing.Intersect(traceRayArgs.Ray)
                                       where isect != null
                                       orderby isect.Dist
                                       let d = isect.Ray.Dir
                                               let pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start)
                                                         let normal = isect.Thing.Normal(pos)
                                                                      let reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal))
                                                                                       let naturalColors =
                                           from light in traceRayArgs.Scene.Lights
                                           let ldis = Vector.Minus(light.Pos, pos)
                                                      let livec = Vector.Norm(ldis)
                                                                  let testRay = new Ray()
            {
                Start = pos, Dir = livec
            }
                                       let testIsects = from inter in
                                                        from thing in traceRayArgs.Scene.Things
                                                        select thing.Intersect(testRay)
                                                        where inter != null
                                                        orderby inter.Dist
                                                        select inter
                                                        let testIsect = testIsects.FirstOrDefault()
                                                                        let neatIsect = testIsect == null ? 0 : testIsect.Dist
                                                                                        let isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0))
                                                                                                         where !isInShadow
                                                                                                         let illum = Vector.Dot(livec, normal)
                                                                                                                     let lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0)
                                                                                                                                  let specular = Vector.Dot(livec, Vector.Norm(reflectDir))
                                                                                                                                                 let scolor = specular > 0
                                                                                         ? Color.Times(Math.Pow(specular, isect.Thing.Surface.Roughness),
                                                                                                       light.Color)
                                                                                         : Color.Make(0, 0, 0)
                                                                                                                                                              select Color.Plus(Color.Times(isect.Thing.Surface.Diffuse(pos), lcolor),
                                                                                                                                                                                Color.Times(isect.Thing.Surface.Specular(pos), scolor))
                                                                                                                                                              let reflectPos = Vector.Plus(pos, Vector.Times(.001, reflectDir))
                                                                                                                                                                               let reflectColor = traceRayArgs.Depth >= MaxDepth
                                                                                          ? Color.Make(.5, .5, .5)
                                                                                          : Color.Times(isect.Thing.Surface.Reflect(reflectPos),
                                                                                                        f(new TraceRayArgs(new Ray()
            {
                Start = reflectPos,
                Dir = reflectDir
            },
                                                                                                                           traceRayArgs.Scene,
                                                                                                                           traceRayArgs.Depth + 1)))
                                                                                                                                                                                                  select naturalColors.Aggregate(reflectColor,
                                                                                                                                                                                                                                 (color, natColor) => Color.Plus(color, natColor))
                                      ).DefaultIfEmpty(Color.Background).First())
                                  let traceRay = Y(computeTraceRay)
                                                 select new { X = x, Y = y, Color = traceRay(new TraceRayArgs(ray, scene, 0)) };

            foreach (var row in pixelsQuery)
            {
                foreach (var pixel in row)
                {
                    setPixel(pixel.X, pixel.Y, pixel.Color);
                }
            }
        }