private Vector3 Trace(Ray Ray) { Medium CurrentMedium = null; LobeType SampledLobe = LobeType.SpecularReflection; Vector3 FinalColor = Vector3.Zero; Vector3 Throughput = Vector3.One; for (int Bounce = 0; Bounce < MaxBounces; Bounce++) { //Raycast to nearest geometry, if any World.Raycast(Ray, out Shape Shape, out Vector3 Hit, out Vector3 Normal, out Vector2 UV); //Return skybox color if nothing hit if (Shape == null) { if (SkyBox == null) { FinalColor += Throughput * Vector3.One; } else { FinalColor += Throughput * SkyBox.GetColorAtUV(new Vector2(1 - (1.0 + Math.Atan2(Ray.Direction.Z, Ray.Direction.X) / MathHelper.Pi) * 0.5, 1 - Math.Acos(Ray.Direction.Y) / MathHelper.Pi)); } break; } //Volumetrics, slightly borken bool Scattered = false; if (CurrentMedium != null) { double MaxDistance = (Hit - Ray.Origin).Length(); double Distance = CurrentMedium.SampleDistance(MaxDistance); Vector3 Transmission = CurrentMedium.Transmission(Distance); Throughput *= Transmission; if (Distance < MaxDistance) { Scattered = true; Ray.Direction = CurrentMedium.SampleDirection(Ray.Direction); Ray.Origin = Hit + Ray.Direction * 0.001; } } //If no scattering event happened, do normal path tracing if (!Scattered) { //Area lights if (Shape.Material.HasProperty("emission")) { //Don't add emission to diffuse term if NEE is on, we already sample it directly if (SampledLobe == LobeType.SpecularReflection || !NEE) { FinalColor += Throughput * Shape.Material.GetProperty("emission", UV).Color; } break; } Vector3 ViewDirection = Vector3.Normalize(Ray.Origin - Hit); //Sample BSDF Shape.Material.Sample(ViewDirection, Normal, UV, out Vector3 SampleDirection, out SampledLobe); Shape.Material.PDF(ViewDirection, Normal, UV, SampleDirection, SampledLobe, out double PDF); Shape.Material.Evaluate(ViewDirection, Normal, UV, SampleDirection, SampledLobe, out Vector3 Attenuation); //Sample direct lighting if (NEE) { FinalColor += Throughput * SampleLight(Shape, Hit, ViewDirection, Normal, UV, SampleDirection, SampledLobe, Attenuation); } //Accumulate BSDF attenuation Throughput *= Attenuation / PDF; //If we entered a medium, update current medium if (SampledLobe == LobeType.SpecularTransmission || SampledLobe == LobeType.DiffuseTransmission) { CurrentMedium = Shape.Material.Medium; } //Set new ray direction to sampled ray Ray.Origin = Hit + SampleDirection * 0.001; Ray.Direction = SampleDirection; } //Russian roulette if (Bounce >= MinBounces) { double Prob = Math.Max(Throughput.X, Math.Max(Throughput.Y, Throughput.Z)); if (Util.Random.NextDouble() > Prob) { break; } Throughput *= 1.0 / Prob; } } return(FinalColor); }