public ColorF CreateAndTraceSecondaryRays(IntersectInfo info, Ray ray, Scene scene, int depth) { // calculate ambient light ColorF infoColorAtHit = ((IPrimitive)info.ClosestHitObject).GetColor(info); ColorF color = infoColorAtHit * scene.background.Ambience; color.alpha = infoColorAtHit.alpha; double shininess = Math.Pow(10, ((IPrimitive)info.ClosestHitObject).Material.Gloss + 1); foreach (ILight light in scene.lights) { // calculate diffuse lighting Vector3 directiorFromHitToLight = light.Origin - info.HitPosition; double distanceToLight = directiorFromHitToLight.Length; Vector3 directiorFromHitToLightNormalized = directiorFromHitToLight.GetNormal(); if (RenderDiffuse) { double l = Vector3Ex.Dot(directiorFromHitToLightNormalized, info.NormalAtHit); if (l > 0.0f) { color += infoColorAtHit * light.Illumination() * 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 && ((IPrimitive)info.ClosestHitObject).Material.Reflection > 0) { Ray reflectionRay = GetReflectionRay(info.HitPosition, info.NormalAtHit, ray.directionNormal); IntersectInfo reflectionInfo = TracePrimaryRay(reflectionRay, scene); ColorF reflectionColorAtHit; // = reflectionInfo.closestHitObject.GetColor(reflectionInfo); if (reflectionInfo.HitType != IntersectionType.None && reflectionInfo.DistanceToHit > 0) { // recursive call, this makes reflections expensive reflectionColorAtHit = CreateAndTraceSecondaryRays(reflectionInfo, reflectionRay, scene, depth + 1); } else // does not reflect an object, then reflect background color { reflectionColorAtHit = scene.background.Color; reflectionColorAtHit.Alpha0To1 = infoColorAtHit.alpha; } color = color.Blend(reflectionColorAtHit, ((IPrimitive)info.ClosestHitObject).Material.Reflection); } // calculate refraction ray if (RenderRefraction && ((IPrimitive)info.ClosestHitObject).Material.Transparency > 0) { var refractionRay = new Ray(info.HitPosition, ray.directionNormal, Ray.sameSurfaceOffset, double.MaxValue); // GetRefractionRay(info.hitPosition, info.normalAtHit, ray.direction, info.closestHit.Material.Refraction); IntersectInfo refractionInfo = TracePrimaryRay(refractionRay, scene); ColorF refractionColorAtHit = ((IPrimitive)refractionInfo.ClosestHitObject).GetColor(refractionInfo); if (refractionInfo.HitType != IntersectionType.None && refractionInfo.DistanceToHit > 0) { // recursive call, this makes refractions expensive refractionColorAtHit = CreateAndTraceSecondaryRays(refractionInfo, refractionRay, scene, depth + 1); } else { refractionColorAtHit = scene.background.Color; refractionColorAtHit.Alpha0To1 = infoColorAtHit.alpha; } color = refractionColorAtHit.Blend(color, ((IPrimitive)info.ClosestHitObject).Material.Transparency); } } var shadow = new IntersectInfo(); if (RenderShadow) { // calculate shadow, create ray from intersection point to light var shadowRay = new Ray(info.HitPosition, directiorFromHitToLightNormalized, Ray.sameSurfaceOffset, double.MaxValue) { isShadowRay = true }; // it may be useful to limit the length to the dist to the camera (but I doubt it LBB). // if the normal at the closest hit is away from the shadow it is already it it's own shadow. if (Vector3Ex.Dot(info.NormalAtHit, directiorFromHitToLightNormalized) < 0) { shadow.HitType = IntersectionType.FrontFace; color *= 0.5; // +0.5 * Math.Pow(shadow.closestHit.Material.Transparency, 0.5); // Math.Pow(.5, shadow.HitCount); color.Alpha0To1 = infoColorAtHit.alpha; } else { // find any element in between intersection point and light shadow = TracePrimaryRay(shadowRay, scene); if (shadow.HitType != IntersectionType.None && shadow.ClosestHitObject != info.ClosestHitObject && shadow.DistanceToHit < distanceToLight) { // only cast shadow if the found intersection is another // element than the current element color *= 0.5; // +0.5 * Math.Pow(shadow.closestHit.Material.Transparency, 0.5); // Math.Pow(.5, shadow.HitCount); color.Alpha0To1 = infoColorAtHit.alpha; } } } // only show highlights if it is not in the shadow of another object if (RenderHighlights && shadow.HitType == IntersectionType.None && ((IPrimitive)info.ClosestHitObject).Material.Gloss > 0) { // only show Gloss light if it is not in a shadow of another element. // calculate Gloss lighting (Phong) Vector3 lv = (info.HitPosition - light.Origin).GetNormal(); Vector3 e = (ray.origin - info.HitPosition).GetNormal(); Vector3 h = (e - lv).GetNormal(); double glossweight = 0.0; glossweight = Math.Pow(Math.Max(Vector3Ex.Dot(info.NormalAtHit, h), 0), shininess); color += light.Illumination() * glossweight; } } color.Clamp0To1(); return(color); }