Colour sample(Scene scene, Ray ray, bool emission, int samples, int depth, Random rand) { if (depth > MaxBounces) { return(Colour.Black); } var hit = scene.Intersect(ray); if (!hit.Ok()) { return(sampleEnvironment(scene, ray)); } var info = hit.Info(ray); var material = info.material; var result = Colour.Black; if (material.Emittance > 0) { if (DirectLighting && !emission) { return(Colour.Black); } result = result.Add(material.Color.MulScalar(material.Emittance * samples)); } var n = (int)Math.Sqrt(samples); BounceType ma, mb; if (SpecularMode == SpecularMode.SpecularModeAll || depth == 0 && SpecularMode == SpecularMode.SpecularModeFirst) { ma = BounceType.BounceTypeDiffuse; mb = BounceType.BounceTypeSpecular; } else { ma = BounceType.BounceTypeAny; mb = BounceType.BounceTypeAny; } for (int u = 0; u < n; u++) { for (int v = 0; v < n; v++) { for (BounceType mode = ma; mode <= mb; mode++) { var fu = (u + ThreadSafeRandom.NextDouble()) / n; var fv = (v + ThreadSafeRandom.NextDouble()) / n; (var newRay, var reflected, var p) = ray.Bounce(info, fu, fv, mode, rand); if (mode == BounceType.BounceTypeAny) { p = 1; } if (p > 0 && reflected) { // specular var indirect = sample(scene, newRay, reflected, 1, depth + 1, rand); var tinted = indirect.Mix(material.Color.Mul(indirect), material.Tint); result = result.Add(tinted.MulScalar(p)); } if (p > 0 && !reflected) { // diffuse var indirect = sample(scene, newRay, reflected, 1, depth + 1, rand); var direct = Colour.Black; if (DirectLighting) { direct = sampleLights(scene, info.Ray, rand); } result = result.Add(material.Color.Mul(direct.Add(indirect)).MulScalar(p)); } } } } return(result.DivScalar(n * n)); }
public void RenderParallel() { Scene scene = Scene; Camera camera = Camera; Sampler sampler = Sampler; Buffer buf = PBuffer; (int w, int h) = (buf.W, buf.H); int spp = SamplesPerPixel; int sppRoot = (int)(Math.Sqrt(SamplesPerPixel)); scene.Compile(); scene.rays = 0; // Stop watch timer Stopwatch sw = new Stopwatch(); sw.Start(); // Random Number Generator from on Math.Numerics System.Random rand = new SystemRandomSource(sw.Elapsed.Milliseconds, true); // Frame resolution int totalPixels = h * w; // Create a cancellation token for Parallel.For loop control CancellationTokenSource cts = new CancellationTokenSource(); // ParallelOptions for Parallel.For ParallelOptions po = new ParallelOptions(); po.CancellationToken = cts.Token; // Set number of cores/threads po.MaxDegreeOfParallelism = Environment.ProcessorCount; Console.WriteLine("{0} x {1} pixels, {2} spp, {3} core(s)", w, h, spp, po.MaxDegreeOfParallelism); if (StratifiedSampling) { _ = Parallel.For(0, w * h, po, (i, loopState) => { int y = i / w, x = i % w; for (int u = 0; u < sppRoot; u++) { for (int v = 0; v < sppRoot; v++) { var fu = (u + 0.5) / sppRoot; var fv = (v + 0.5) / sppRoot; Ray ray = camera.CastRay(x, y, w, h, fu, fv, rand); Colour sample = sampler.Sample(scene, ray, rand); buf.AddSample(x, y, sample); } } }); Console.WriteLine("time elapsed:" + sw.Elapsed); } else { //Random subsampling _ = Parallel.ForEach(Partitioner.Create(0, totalPixels), po, (range) => { for (int i = range.Item1; i < range.Item2; i++) { for (int s = 0; s < spp; s++) { int y = i / w, x = i % w; var fu = ThreadSafeRandom.NextDouble(rand); var fv = ThreadSafeRandom.NextDouble(rand); var ray = camera.CastRay(x, y, w, h, fu, fv, rand); var sample = sampler.Sample(scene, ray, rand); buf.AddSample(x, y, sample); } } }); Console.WriteLine("time elapsed:" + sw.Elapsed); } if (AdaptiveSamples > 0) { _ = Parallel.For(0, w * h, po, (i, loopState) => { int y = i / w, x = i % w; double v = buf.StandardDeviation(x, y).MaxComponent(); v = Util.Clamp(v / AdaptiveThreshold, 0, 1); v = Math.Pow(v, AdaptiveExponent); int samples = (int)(v * AdaptiveSamples); for (int s = 0; s < samples; s++) { var fu = rand.NextDouble(); var fv = rand.NextDouble(); Ray ray = camera.CastRay(x, y, w, h, fu, fv, rand); Colour sample = sampler.Sample(scene, ray, rand); buf.AddSample(x, y, sample); } }); } if (FireflySamples > 0) { _ = Parallel.For(0, w * h, po, (i, loopState) => { int y = i / w, x = i % w; if (PBuffer.StandardDeviation(x, y).MaxComponent() > FireflyThreshold) { Parallel.For(0, FireflySamples, po, (e, loop) => { var fu = rand.NextDouble(); var fv = rand.NextDouble(); Ray ray = camera.CastRay(x, y, w, h, fu, fv, rand); Colour sample = sampler.Sample(scene, ray, rand); buf.AddSample(x, y, sample); }); } }); } sw.Stop(); }