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); }
/// <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); }