/// <summary> /// Traces the ray if its inside an object which means its probably in refraction loop /// </summary> /// <param name="ray"></param> /// <param name="obj"></param> /// <param name="debug"></param> /// <returns></returns> protected Vector3 traceRayInside(Ray ray, Primitive obj, bool debug) { if (ray.isOutside()) { return(TraceRay(ray, debug)); // Should not happen anyway } Vector3 ret = Vector3.Zero; float multiplier = 1; float absorb_distance = 0; // Do the refraction loop. Aka refract, reflect, repeat RayHit hit = RayHit.Default(); while (ray.Depth < Settings.MaxDepth - 1 && multiplier > Constants.EPSILON) { if (!obj.Intersect(ray, ref hit)) { return(ret); // Should not happen either } if (debug) { DebugData.RefractRays.Add(new Tuple <Ray, RayHit>(ray, hit)); } // Beer absorption absorb_distance += hit.T; var absorb = QuickMaths.Exp(-obj.Material.Absorb * absorb_distance); // Fresnel var reflect_multiplier = QuickMaths.Fresnel(obj.Material.RefractionIndex, Constants.LIGHT_IOR, ray.Direction, hit.Normal); var refract_multiplier = 1f - reflect_multiplier; // refract if its worth if (refract_multiplier > Constants.EPSILON) { ret += TraceRay(RayTrans.Refract(ray, hit), debug) * refract_multiplier * multiplier * absorb; } ray = RayTrans.Reflect(ray, hit); multiplier *= reflect_multiplier; } return(ret); }
/// <summary> /// Trace the ray and go around the world in 80ns. Or more :S /// </summary> /// <param name="ray"></param> /// <param name="debug"></param> /// <returns></returns> public Vector3 TraceRay(Ray ray, bool debug = false) { Vector3 ret = Vector3.Zero; if (ray.Depth > Settings.MaxDepth) { return(ret); } // Check for intersections RayHit hit = intersect(ray); if (hit.Obj == null) { return(World.Environent == null ? Vector3.Zero : World.Environent.GetColor(ray)); } if (debug) { DebugData.PrimaryRays.Add(new Tuple <Ray, RayHit>(ray, hit)); } // Calculate color and specular highlights. Pure mirrors dont have diffuse Vector3 specular = Vector3.Zero; Vector3 color = Vector3.Zero; if (!hit.Obj.Material.IsMirror) { color += illuminate(ray, hit, ref specular, debug); color += World.Environent.AmbientLight; } // Different materials are handled differently. Would be cool to move that into material if (hit.Obj.Material.IsMirror) { ret = TraceRay(RayTrans.Reflect(ray, hit), debug); } else if (hit.Obj.Material.IsDielectic) { var n1 = ray.isOutside() ? Constants.LIGHT_IOR : hit.Obj.Material.RefractionIndex; // TODO: shorten this shizzle into a func var n2 = ray.isOutside() ? hit.Obj.Material.RefractionIndex : Constants.LIGHT_IOR; float reflect_multiplier = QuickMaths.Fresnel(n1, n2, hit.Normal, ray.Direction); reflect_multiplier = hit.Obj.Material.Reflectivity + (1f - hit.Obj.Material.Reflectivity) * reflect_multiplier; float transmission_multiplier = 1 - reflect_multiplier; // Reflect if its worth it if (reflect_multiplier > Constants.EPSILON) { Ray reflRay = RayTrans.Reflect(ray, hit); uint takeSamples = 0; if (hit.Obj.Material.Roughness > 0.001f && ray.Refldepth <= 1) { takeSamples = Settings.MaxReflectionSamples; } else { takeSamples = 1; } //Take many samples for rough reflections for (int i = 0; i < takeSamples; i++) { Ray localRay = reflRay; localRay.Direction = RRandom.RandomChange(reflRay.Direction, hit.Obj.Material.Roughness); ret += reflect_multiplier * TraceRay(localRay, debug); } ret /= takeSamples; } if (transmission_multiplier > Constants.EPSILON) { if (hit.Obj.Material.IsRefractive) { var tmp_ray = RayTrans.Refract(ray, hit); if (!float.IsNaN(tmp_ray.Direction.X)) // Happens rarely { ret += transmission_multiplier * traceRayInside(tmp_ray, hit.Obj, debug); } } else { ret += transmission_multiplier * color; } } } else { // Standard diffuse ret = color; } return(ret + specular); }