// Obtain the colour of a ray/model intersection
        public Colour Shade(Intersection hit, Ray r, int level, double weight)
        {
            if (level > traceMax)
            {
                return(background); // Too deep
            }
            // Get the surface normal
            var normal = hit.Model.Normal(hit.Location);

            // If looking at the inside of a model (e.g. rhs of CSGDifference; or transparent object)
            // then the normal will be inverted
            if ((normal * r.Direction) > 0.0)
            {
                normal = -normal;
            }

            var colour = new Colour();

            // Point p, where shadow ray originates, is elevated from the model
            // surface to prevent erroneous self-shadowing.
            var p      = hit.Location + normal * rayOffset;
            var filter = new Colour(1.0);

            foreach (var l in lights)
            {
                // If shadowing, determine if l is blocked
                // If only blocked by transparent objects then return the
                // overall filter colour in filter;
                if (!(shadow && Shadowed(p, l, out filter)))
                {
                    colour += (filter * (l.Illumination(hit, normal)));
                }
            }

            var materialHit = hit.Model.Material;

            if (materialHit == null)
            {
                materialHit = Material.NullMaterial;
            }

            // Add ambient light
            colour += ambient * materialHit.GetPigment(hit.Location);

            var reflectance = materialHit.Reflectance;

            // Do reflections, but only if they will make a visible contribution
            if ((reflectance != 0.0) &&
                (reflectance * weight > minWeight) &&
                (level <= traceMax))
            {
                // launch reflected ray from rayOffset above the surface
                var rray = new Ray(hit.Location + normal * rayOffset,
                                   (normal * (-2.0 * (r.Direction * normal)) +
                                    r.Direction));

                var rhit = Intersect(rray);
                if (rhit.Model != null)
                {
                    if (rhit.Model.Material == null)
                    {
                        throw new AuroraException("Scene.Trace  Model has null material");
                    }

                    colour += Shade(rhit, rray, level + 1, reflectance * weight) *
                              reflectance;
                }
                // Reflected into space
                else
                {
                    colour += background * reflectance;
                }
            }

            if (materialHit.Transparent)
            {
                // The transmitted (or internally reflected) ray
                Ray tray;
                // The medium for tray
                var medium = materialHit;

                if (materialHit.Ior != hit.Medium.Ior) // Refracted?
                {
                    var eta  = hit.Medium.Ior / materialHit.Ior;
                    var ci   = normal * -r.Direction;
                    var disc = 1.0 + eta * eta * (ci * ci - 1.0);

                    // disc discriminates between refraction and tir
                    if (disc < 0.0)
                    {
                        // TODO: Why did this originally just return White?
                        // Total internal reflection
                        // launch transmitted ray from rayOffset above the surface
                        tray   = new Ray(hit.Location + normal * rayOffset, (normal * (-2.0 * (r.Direction * normal)) + r.Direction));
                        medium = hit.Medium;
                        // return new Colour(1.0, 1.0, 0.0);
                    }
                    else
                    {
                        // Refraction
                        // launch transmitted ray from rayOffset beyond the surface
                        var tdir = (eta * r.Direction + (eta * ci - Math.Sqrt(disc)) * normal).Normalise();
                        tray = new Ray(hit.Location + tdir * rayOffset, tdir);
                    }
                }
                else
                {
                    // Simple transmission (identical refractive indices)
                    tray = new Ray(hit.Location + r.Direction * rayOffset, r.Direction);
                }
                var thit = Intersect(tray);
                if (thit.Model != null)
                {
                    var w = medium.Filter.Lightness() * weight;
                    colour += Shade(thit, tray, level + 1, w) * medium.Filter;
                }
                else
                {
                    colour += background * medium.Filter;
                }
            }
            return(colour);
        }
 public void MakeTransparent(Colour tfilter, double tior)
 {
     filter      = tfilter;
     ior         = tior;
     transparent = true;
 }
 public Pigment(Colour d)
 {
     a = d;
 }
 public Material(Colour c, Finish f) : this(new Pigment(c), f)
 {
 }