private Vector3 CalculateNaturalColor(
            Vector3 position,
            Vector3 intersectionNormal,
            ITracingOptions tracingOptions,
            ISurface surface,
            int sampleIndex)
        {
            // use Phong shading model to determine color
            // https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model

            // use minimal ambient
            var color = new Vector3(0.2f);

            foreach (var light in tracingOptions.Scene.Lights)
            {
                var offset = Vector3.Zero;
                if (sampleIndex > 0)
                {
                    const float offsetFactor = 0.1f;
                    offset = new Vector3(-offsetFactor + offsetFactor * 2 * (float)_random.NextDouble(),
                                         -offsetFactor + offsetFactor * 2 * (float)_random.NextDouble(),
                                         -offsetFactor + offsetFactor * 2 * (float)_random.NextDouble());
                }
                var lightDistance = light.Position + offset - position;
                var lightDir      = Vector3.Normalize(lightDistance);

                // check if light source is reachable from current position or not
                // must move away from object slightly, otherwise collision will be current point
                var ray = new Ray(position + lightDir * 0.001f, lightDir);
                var ix  = CheckIntersection(ray, tracingOptions.Scene);
                if (ix.HasValue)
                {
                    var intersection = ix.Value;
                    var isInShadow   = intersection.Distance * intersection.Distance < lightDistance.LengthSquared();
                    if (isInShadow)
                    {
                        continue;
                    }
                }

                var illumination = MathHelper.Clamp(Vector3.Dot(lightDir, intersectionNormal), 0, float.MaxValue);
                var c            = illumination * light.Color.ToVector3() * light.Intensity;
                color += c * surface.Diffuse(position);

                var specular = MathHelper.Clamp(Vector3.Dot(lightDir, intersectionNormal), 0, float.MaxValue);
                color += specular * c * (float)Math.Pow(specular, surface.Shininess) * surface.Specular(position);
            }
            return(color / tracingOptions.Scene.Lights.Count);
        }
Esempio n. 2
0
        /// <summary>
        /// Calculates the color at the specific location.
        /// </summary>
        /// <param name="posOnObject"></param>
        /// <param name="normal"></param>
        /// <param name="scene"></param>
        /// <param name="surface">The surface that was hit.</param>
        /// <returns></returns>
        private Vector3 CalculateNaturalColor(Vector3 posOnObject, Vector3 normal, Scene scene, ISurface surface, int sampleId)
        {
            var ret = new Vector3(0.1f);

            foreach (var light in scene.Lights)
            {
                var     lPos   = light.Position;
                Vector3 rndVec = Vector3.Zero;
                if (sampleId > 1)
                {
                    // apply random offset for light source (this will create a slightly different light direction and thus a slightly different light bounce direction)
                    // and thus in turn create soft shadows
                    // note that we only apply this random offset to samples 2 and on
                    // if the user selected only a single sample then we don't create offset and thus allow him to raytrace with hard shadows

                    // offset light by +- factor in XYZ
                    // lights can be anywhere in the box of [pos - vec(factor);pos + vec(factor)]
                    const float factor = 0.1f;
                    rndVec = new Vector3(-factor + Random() * factor * 2, -factor + Random() * factor * 2, -factor + Random() * factor * 2);
                }
                lPos += rndVec;
                var lightDistance = lPos - posOnObject;
                var lightDir      = Vector3.Normalize(lightDistance);

                // check if there is another object between the light source and the position for which to calculate the lighting
                // if so, then this light doesn't actually hit the object, so it can be ignored
                var intersects = CheckRayIntersection(new Ray(posOnObject + lightDir * 0.001f, lightDir), scene);
                if (intersects.HasValue)
                {
                    var  distance   = intersects.Value.Distance;
                    bool isInShadow = distance <= lightDistance.Length();
                    // ignore light if the object is in the shadow of another object
                    if (isInShadow)
                    {
                        continue;
                    }
                }
                // calculate brightness
                var illumination = Vector3.Dot(lightDir, normal);
                var c            = light.Color.ToVector3() * light.Intensity;
                var color        = illumination > 0 ? c * illumination : Vector3.Zero;
                ret += color * surface.Diffuse(posOnObject);

                var specular      = Vector3.Dot(lightDir, normal);
                var specularColor = specular > 0 ? c * (float)Math.Pow(specular, surface.Shininess) : Vector3.Zero;
                ret += specularColor * surface.Specular(posOnObject);
            }
            return(ret);
        }