/// <summary> /// This implementation of intersect uses the fastest ray-sphere intersection algorithm I could find /// on the internet. /// </summary> /// <param name="ray"></param> /// <returns></returns> public override IntersectInfo Intersect(Ray ray) { IntersectInfo info = new IntersectInfo(); info.Element = this; Vector dst = ray.Position - this.Position; double B = dst.Dot(ray.Direction); double C = dst.Dot(dst) - (R * R); double D = B * B - C; if (D > 0) // yes, that's it, we found the intersection! { info.IsHit = true; info.Distance = -B - (double)Math.Sqrt(D); info.Position = ray.Position + ray.Direction * info.Distance; info.Normal = (info.Position - Position).Normalize(); if (Material.HasTexture) { Vector vn = new Vector(0, 1, 0).Normalize(); // north pole / up Vector ve = new Vector(0, 0, 1).Normalize(); // equator / sphere orientation Vector vp = (info.Position - Position).Normalize(); //points from center of sphere to intersection double phi = Math.Acos(-vp.Dot(vn)); double v = (phi * 2 / Math.PI) - 1; double sinphi = ve.Dot(vp) / Math.Sin(phi); sinphi = sinphi < -1 ? -1 : sinphi > 1 ? 1 : sinphi; double theta = Math.Acos(sinphi) * 2 / Math.PI; double u; if (vn.Cross(ve).Dot(vp) > 0) { u = theta; } else { u = 1 - theta; } // alternative but worse implementation //double u = Math.Atan2(vp.x, vp.z); //double v = Math.Acos(vp.y); info.Color = this.Material.GetColor(u, v); } else { // skip uv calculation, just get the color info.Color = this.Material.GetColor(0, 0); } } else { info.IsHit = false; } return(info); }
private IntersectInfo IntersectSlab(Ray ray, Vector P1, Vector P2, Vector P3) { Vector N = (P1 - P3).Cross(P2 - P3).Normalize(); double d = N.Dot(P1); IntersectInfo info = new IntersectInfo(); double Vd = N.Dot(ray.Direction); if (Vd == 0) { return(info); // no intersection } double t = -(N.Dot(ray.Position) - d) / Vd; if (t <= 0) { return(info); } Vector hit = ray.Position + ray.Direction * t; if ((hit.x < P1.x || hit.x > P2.x) && (P1.x != P2.x)) { return(info); } if ((hit.y < P3.y || hit.y > P1.y) && (P1.y != P2.y)) { return(info); } //if ((hit.z < P1.z || hit.z > P3.z) && (P1.z != P3.z)) return info; if ((hit.z < P1.z || hit.z > P2.z) && (P1.z != P2.z)) { return(info); } info.Element = this; info.IsHit = true; info.Position = hit; info.Normal = N;// *-1; info.Distance = t; if (Material.HasTexture) { //Vector vecU = new Vector(hit.y - Position.y, hit.z - Position.z, Position.x-hit.x); Vector vecU = new Vector((P1.y + P2.y) / 2 - Position.y, (P1.z + P2.z) / 2 - Position.z, Position.x - (P1.x + P2.x) / 2).Normalize(); Vector vecV = vecU.Cross((P1 + P2) / 2 - Position).Normalize(); double u = info.Position.Dot(vecU); double v = info.Position.Dot(vecV); info.Color = Material.GetColor(u, v); } else { info.Color = Material.GetColor(0, 0); } return(info); }
/// <summary> /// this implementation is used for debugging purposes. /// the color is calculated following the normal raytrace procedure /// execpt it is calculated for 1 particula ray /// </summary> /// <param name="ray">the ray for which to calculate the color</param> /// <param name="scene">the scene which is raytraced</param> /// <returns></returns> public Color CalculateColor(Ray ray, Scene scene) { IntersectInfo info = TestIntersection(ray, scene, null); if (info.IsHit) { // execute the actual raytrace algorithm Color c = RayTrace(info, ray, scene, 0); return(c); } return(scene.Background.Color); }
public override IntersectInfo Intersect(Ray ray) { IntersectInfo info = new IntersectInfo(); double Vd = Position.Dot(ray.Direction); if (Vd == 0) { return(info); // no intersection } double t = -(Position.Dot(ray.Position) + D) / Vd; if (t <= 0) { return(info); } info.Element = this; info.IsHit = true; info.Position = ray.Position + ray.Direction * t; info.Normal = Position;// *-1; info.Distance = t; if (Material.HasTexture) { Vector vecU = new Vector(Position.y, Position.z, -Position.x); Vector vecV = vecU.Cross(Position); double u = info.Position.Dot(vecU); double v = info.Position.Dot(vecV); info.Color = Material.GetColor(u, v); } else { info.Color = Material.GetColor(0, 0); } return(info); }
/// <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) { int hitcount = 0; IntersectInfo best = new IntersectInfo(); best.Distance = double.MaxValue; foreach (IShape elt in scene.Shapes) { if (elt == exclude) { continue; } IntersectInfo info = elt.Intersect(ray); if (info.IsHit && info.Distance < best.Distance && info.Distance >= 0) { best = info; hitcount++; } } best.HitCount = hitcount; return(best); }
public override IntersectInfo Intersect(Ray ray) { IntersectInfo best = new IntersectInfo(); best.Distance = double.MaxValue; IntersectInfo info = null; //check each side of the box // this is quite a crude implementation and can be optimized Vector P1 = null; Vector P2 = null; Vector P3 = null; if (ray.Direction.z > 0) { //front P1 = UpperLeftFront; P2 = new Vector(LowerRightBack.x, LowerRightBack.y, P1.z); P3 = new Vector(P1.x, P2.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } else { //backside P1 = new Vector(UpperLeftFront.x, UpperLeftFront.y, LowerRightBack.z); P2 = LowerRightBack; P3 = new Vector(P1.x, P2.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } if (ray.Direction.x < 0) { //right side P1 = new Vector(LowerRightBack.x, UpperLeftFront.y, UpperLeftFront.z); P2 = LowerRightBack; P3 = new Vector(P1.x, P2.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } else { //left side P1 = UpperLeftFront; P2 = new Vector(UpperLeftFront.x, LowerRightBack.y, LowerRightBack.z); P3 = new Vector(P1.x, P2.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } if (ray.Direction.y < 0) { //top side P1 = UpperLeftFront; P2 = new Vector(LowerRightBack.x, UpperLeftFront.y, LowerRightBack.z); P3 = new Vector(P2.x, P1.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } else { //bottom side P1 = new Vector(UpperLeftFront.x, LowerRightBack.y, UpperLeftFront.z); P2 = LowerRightBack; P3 = new Vector(P2.x, P1.y, P1.z); info = IntersectSlab(ray, P1, P2, P3); if (info.IsHit && info.Distance < best.Distance) { best = info; } } 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 Color RayTrace(IntersectInfo info, Ray ray, Scene scene, int depth) { // calculate ambient light Color color = info.Color * scene.Background.Ambience; double shininess = Math.Pow(10, info.Element.Material.Gloss + 1); foreach (Light light in scene.Lights) { // calculate diffuse lighting Vector v = (light.Position - info.Position).Normalize(); if (RenderDiffuse) { double L = v.Dot(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 < 3) { // calculate reflection ray if (RenderReflection && info.Element.Material.Reflection > 0) { Ray reflectionray = GetReflectionRay(info.Position, info.Normal, ray.Direction); IntersectInfo 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 (RenderRefraction && info.Element.Material.Transparency > 0) { Ray refractionray = GetRefractionRay(info.Position, info.Normal, ray.Direction, info.Element.Material.Refraction); IntersectInfo 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); } } IntersectInfo shadow = new IntersectInfo(); if (RenderShadow) { // calculate shadow, create ray from intersection point to light Ray 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 (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) Vector Lv = (info.Element.Position - light.Position).Normalize(); Vector E = (scene.Camera.Position - info.Element.Position).Normalize(); Vector H = (E - Lv).Normalize(); double Glossweight = 0.0; Glossweight = Math.Pow(Math.Max(info.Normal.Dot(H), 0), shininess); color += light.Color * (Glossweight); } } // normalize the color color.Limit(); return(color); }