/// <summary> /// this method tests for an intersection. It will try to find the closest /// object that intersects with the ray. /// it will inspect every object in the scene. also here there is room for increased performance. /// </summary> /// <param name="ray"></param> /// <param name="scene"></param> /// <param name="exclude"></param> /// <returns></returns> private IntersectInfo TestIntersection(Ray ray, Scene scene, IShape exclude) { var hitcount = 0; var best = new IntersectInfo() { Distance = double.MaxValue }; foreach (var elt in scene.Shapes) { if (elt == exclude) { continue; } var info = elt.Intersect(ray); if (info.IsHit && info.Distance < best.Distance && info.Distance >= 0) { best = info; hitcount++; } } best.HitCount = hitcount; return(best); }
/// <summary> /// This is the main RayTrace controller algorithm, the core of the RayTracer /// recursive method setup /// this does the actual tracing of the ray and determines the color of each pixel /// supports: /// - ambient lighting /// - diffuse lighting /// - Gloss lighting /// - shadows /// - reflections /// </summary> /// <param name="info"></param> /// <param name="ray"></param> /// <param name="scene"></param> /// <param name="depth"></param> /// <returns></returns> private Vector RayTrace(IntersectInfo info, Ray ray, Scene scene, int depth) { // calculate ambient light var color = info.Color * scene.Background.Ambience; // Console.WriteLine($"color ({color}) from info.color({info.Color})"); var shininess = Math.Pow(10, info.Element.Material.Gloss + 1); foreach (var light in scene.Lights) { // calculate diffuse lighting var v = (light.Position - info.Position).Normalize(); if (scene.RenderDiffuse) { var l = v.DotProduct(info.Normal); if (l > 0.0f) { color += info.Color * light.Color * l; } } // this is the max depth of raytracing. // increasing depth will calculate more accurate color, however it will // also take longer (exponentially) if (depth < _traceDepth) { // calculate reflection ray if (scene.RenderReflection && info.Element.Material.Reflection > 0) { var reflectionray = GetReflectionRay(info.Position, info.Normal, ray.Direction); var refl = TestIntersection(reflectionray, scene, info.Element); if (refl.IsHit && refl.Distance > 0) { // recursive call, this makes reflections expensive refl.Color = RayTrace(refl, reflectionray, scene, depth + 1); } else // does not reflect an object, then reflect background color { refl.Color = scene.Background.Color; } color = color.Blend(refl.Color, info.Element.Material.Reflection); } //calculate refraction ray if (scene.RenderRefraction && info.Element.Material.Transparency > 0) { var refractionray = GetRefractionRay(info.Position, info.Normal, ray.Direction, info.Element.Material.Refraction); var refr = info.Element.Intersect(refractionray); if (refr.IsHit) { //refractionray = new Ray(refr.Position, ray.Direction); refractionray = GetRefractionRay(refr.Position, refr.Normal, refractionray.Direction, refr.Element.Material.Refraction); refr = TestIntersection(refractionray, scene, info.Element); if (refr.IsHit && refr.Distance > 0) { // recursive call, this makes refractions expensive refr.Color = RayTrace(refr, refractionray, scene, depth + 1); } else { refr.Color = scene.Background.Color; } } else { refr.Color = scene.Background.Color; } color = color.Blend(refr.Color, info.Element.Material.Transparency); } } var shadow = new IntersectInfo(); if (scene.RenderShadow) { // calculate shadow, create ray from intersection point to light var shadowray = new Ray(info.Position, v); // find any element in between intersection point and light shadow = TestIntersection(shadowray, scene, info.Element); if (shadow.IsHit && shadow.Element != info.Element) { // only cast shadow if the found interesection is another // element than the current element color *= 0.5 + 0.5 * Math.Pow(shadow.Element.Material.Transparency, 0.5); // Math.Pow(.5, shadow.HitCount); } } // only show highlights if it is not in the shadow of another object if (scene.RenderHighlights && !shadow.IsHit && info.Element.Material.Gloss > 0) { // only show Gloss light if it is not in a shadow of another element. // calculate Gloss lighting (Phong) var lv = (info.Element.Position - light.Position).Normalize(); var e = (scene.Camera.Position - info.Element.Position).Normalize(); var h = (e - lv).Normalize(); var glossweight = 0.0; glossweight = Math.Pow(Math.Max(info.Normal.DotProduct(h), 0), shininess); color += light.Color * (glossweight); } } return(color); }