public override double3 CalculateColor(Scene scene, Traceable traceable, IntersectData data, Ray ray, TraceData traceData) { double3 resultColor = double3.Zero; double3 n = traceable.GetNormal(data); foreach (SpotLight light in scene.Lights) { double3 l = light.Pos - data.P; double distance = l.Length; l = l.Normalized; double nDotL = n & l; if (nDotL <= 0) { continue; } double shadowEdgeAttenuation = 1; List <IntersectData> shadowIsecs = scene.Intersect(new Ray(traceable.Advance(data.P, l), l)); if (shadowIsecs.Count > 0) { IntersectData obstacleIsecData = shadowIsecs.OrderBy(shadowIsecData => (shadowIsecData.P - data.P).Length).First(); if ((obstacleIsecData.P - data.P).Length < distance) { double3 obstacleL = light.Pos - obstacleIsecData.P; obstacleL = obstacleL.Normalized; double3 obstacleN = obstacleIsecData.Object.GetNormal(obstacleIsecData); double3 obstacleR = l.ReflectI(obstacleN); shadowEdgeAttenuation = obstacleR & obstacleL; if (shadowEdgeAttenuation <= 0) { continue; } shadowEdgeAttenuation = Math.Pow(shadowEdgeAttenuation, 3); } } double3 diffuse = BaseColor ^ light.DiffuseColor * nDotL; double3 r = l.Reflect(n, nDotL); double rDotV = Math.Max(0, (-ray.l) & r); double3 specular = Math.Pow(rDotV, SpecularPower) * light.SpecularColor; double attenuation = Math.Log(distance + light.AttenuationBase, light.AttenuationBase); resultColor += (diffuse + specular) * shadowEdgeAttenuation / attenuation; } if (traceData.Reflections > 0) { double3 r = ray.l.ReflectI(n); double3 p = traceable.Advance(data.P, r); double3 reflectionColor = scene.Trace(new Ray(p, r), traceData.GetReflected()); resultColor += reflectionColor * ReflectionAttenuation; } else { TraceData.ReflectionLimitExceedCount++; return(TraceData.ReflectionLimitExceedColor); } return(resultColor + scene.AmbientColor); }