/// <summary> Sample the <paramref name="scene"/> with a <paramref name="sample"/> returning a color found </summary> /// <param name="scene">The <see cref="IScene"/> to sample </param> /// <param name="ray">The <see cref="Ir4"/> to trace through the <paramref name="scene"/> </param> /// <param name="spectrum">The throughput <see cref="ISpectrum"/></param> /// <param name="recursionDepth">The depth of recursion</param> /// <returns>The color found for the <see cref="ISample"/></returns> public ISpectrum Sample(IScene scene, IRay ray, ISpectrum spectrum, int recursionDepth) { if (spectrum.Equals(ISpectrum.Black)) { return(ISpectrum.Black); } /// Russian Roulette float throughput = 1f; if (recursionDepth >= GauranteedRecursionDepth) { if (Utils.ThreadRandom.NextSingle() < RussianRouletteChance) { return(ISpectrum.Black); } else { throughput = 1f / RussianRouletteChance; } } /// Sample Distance IDistanceQuery?distanceQuery = scene.Trace(ray, spectrum); if (distanceQuery is null) { return(ISpectrum.Black); } Position1 distance = distanceQuery.DistanceDistribution.Sample(Utils.ThreadRandom); if (distance == Position1.PositiveInfinity) { return(ISpectrum.Black); } /// Sample Primitive IProbabilityDistribution <IPrimitive>?primitives = distanceQuery.TryGetPrimitives(distance); if (primitives is null) { throw new InvalidOperationException("Distance was sampled but no primitive was found"); } IPrimitive primitive = primitives.Sample(Utils.ThreadRandom); /// Get Intersection Position Position3 position = primitive.Material.DensityProfile.GetPosition(ray, distance, primitive.Shape); /// Sample Material Orientation IProbabilityDistribution <Normal3>?orientations = primitive.Material.OrientationProfile.GetOrientations(position, ray.Direction, primitive.Shape); if (orientations is null) { return(ISpectrum.Black); } Normal3 orientation = orientations.Sample(Utils.ThreadRandom); /// Get Direct Illumination ISpectrum directIllumination = RGBColors.Black; if (primitive.Material.EmittanceProfile.IsEmitting) { directIllumination = primitive.Material.EmittanceProfile.GetEmittance(position, orientation, -ray.Direction); } /// Get Indirect Illumination ISpectrum indirectIllumination = RGBColors.Black; if (!primitive.Material.AbsorptionProfile.IsBlackBody) { /// Sample Direction IProbabilityDistribution <Normal3> directions = primitive.Material.ReflectionProfile.GetDirections(ray.Direction, position, orientation, spectrum); Normal3 direction = directions.Sample(Utils.ThreadRandom); /// Get Albedo ISpectrum albedo = primitive.Material.AbsorptionProfile.GetAlbedo(position, orientation, -direction); /// Get Ray IRay raySample = primitive.Material.DensityProfile.GetRay(position, orientation, direction); /// Sample Indirect Illumination indirectIllumination = albedo * Sample(scene, raySample, spectrum * albedo, recursionDepth + 1); } /// Light Throughput Calculation return((directIllumination + indirectIllumination) * throughput); }