public RtColor ShadeHit(Computations computations, int remaining)
        {
            var colorTotal = RtColor.Black;

            foreach (var light in Lights)
            {
                // var inShadow = IsShadowed(light.Position, computations.OverPosition);
                var intensity = light.IntensityAt(computations.OverPosition, this);
                var surface   = computations.Shape.Material.Lighting(
                    computations,
                    light,
                    intensity);

                var reflected = ReflectedColor(computations, remaining);
                var refracted = RefractedColor(computations, remaining);

                var material = computations.Shape.Material;
                if (material.Reflective > 0 && material.Transparency > 0)
                {
                    var reflectance = computations.Schlick();
                    colorTotal += surface + reflected * reflectance + refracted * (1 - reflectance);
                }
                else
                {
                    colorTotal += surface + reflected + refracted;
                }
            }

            return(colorTotal);
        }
        public RtColor ReflectedColor(Computations computations, int remaining)
        {
            if (computations.Shape.Material.Reflective == 0 || remaining <= 0)
            {
                return(RtColor.Black);
            }

            var reflectRay   = new Ray(computations.OverPosition, computations.ReflectVector);
            var reflectColor = ColorAt(reflectRay, remaining - 1);

            return(reflectColor * computations.Shape.Material.Reflective);
        }
        public RtColor Lighting(Computations computations, Light light, double intensity)
        {
            var color = Color;

            if (Pattern != null)
            {
                color = Pattern.PatternAtShape(computations.Shape, computations.OverPosition);
            }

            var effectiveColor = color * light.Intensity;
            var ambient        = effectiveColor * Ambient;

            // var lightPosition = light.Position;

            RtColor sum = RtColor.Black;

            foreach (var lightPosition in light.GetSamples())
            {
                var lightVector    = (lightPosition - computations.Position).Normalize();
                var lightDotNormal = lightVector.Dot(computations.NormalVector);

                if (lightDotNormal < 0 || intensity == 0)
                {
                    continue;
                }

                sum += effectiveColor * Diffuse * lightDotNormal;

                var reflectVector = lightVector.Negate().Reflect(computations.NormalVector);
                var reflectDotEye = reflectVector.Dot(computations.EyeVector);

                if (reflectDotEye > 0)
                {
                    var factor = Math.Pow(reflectDotEye, Shininess);
                    sum += light.Intensity * Specular * factor;
                }
            }

            var results = ambient + (sum / light.Samples) * intensity;

            return(results);
        }
        public RtColor RefractedColor(Computations computations, int remaining)
        {
            if (computations.Shape.Material.Transparency == 0 || remaining == 0)
            {
                return(RtColor.Black);
            }

            var nRatio = computations.n1 / computations.n2;
            var cosi   = computations.EyeVector.Dot(computations.NormalVector);
            var sin2t  = Math.Pow(nRatio, 2) * (1 - Math.Pow(cosi, 2));

            if (sin2t > 1)
            {
                return(RtColor.Black);
            }

            var cost       = Math.Sqrt(1.0 - sin2t);
            var direction  = computations.NormalVector * (nRatio * cosi - cost) - computations.EyeVector * nRatio;
            var refractRay = new Ray(computations.UnderPosition, direction);

            var color = ColorAt(refractRay, remaining - 1) * computations.Shape.Material.Transparency;

            return(color);
        }
        public Computations PrepareComputations(Ray ray, IntersectionList intersections)
        {
            var computations = new Computations()
            {
                Time = Time,
                Shape = Shape,
                Position = ray.Position(Time),
                EyeVector = ray.Direction.Negate(),
            };

            computations.NormalVector = Shape.NormalAt(computations.Position, this);

            if(computations.NormalVector.Dot(computations.EyeVector) < 0)
            {
                computations.Inside = true;
                computations.NormalVector = computations.NormalVector.Negate();
            }
            else
            {
                computations.Inside = false;
            }

            computations.ReflectVector = ray.Direction.Reflect(computations.NormalVector);

            List<Shape> container = new List<Shape>();

            if(intersections != null && intersections.Count > 0)
            {
                foreach (var intersection in intersections.Items)
                {
                    if (intersection == this)
                    {
                        if (container.Count == 0)
                        {
                            computations.n1 = 1.0;
                        }
                        else
                        {
                            computations.n1 = container.Last().Material.RefractiveIndex;
                        }
                    }

                    if (container.Contains(intersection.Shape))
                    {
                        container.Remove(intersection.Shape);
                    }
                    else
                    {
                        container.Add(intersection.Shape);
                    }

                    if (intersection == this)
                    {
                        if (container.Count == 0)
                        {
                            computations.n2 = 1.0;
                        }
                        else
                        {
                            computations.n2 = container.Last().Material.RefractiveIndex;
                        }

                        break;
                    }
                }
            }

            return computations;
        }