private HitPoint FindClosestHitPoint(Ray ray) { var hitPoints = new List <HitPoint>(); if (AccelerationStructure) { BVHNode root = _accelerationStructure; hitPoints.AddRange(FindHitpoints(ray, root)); } else { for (int i = 0; i < _spheres.Length; i++) { var sphere = _spheres[i]; var hitPoint = FindHitpoint(ray, sphere); if (hitPoint != null) { hitPoints.Add(hitPoint); } } } return(hitPoints.OrderBy(h => h.Ray.Lambda).FirstOrDefault()); }
public static void RecursivePrint(BVHNode node, Action <string> logger, int indent = 0) { if (node != null) { var sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.Append(" "); } var indentStr1 = sb.ToString(); var indentStr2 = indentStr1 + " "; var sphere = node.BoundingSphere; logger(indentStr1 + FormatSphere(sphere)); foreach (var item in node.Items) { logger(indentStr2 + FormatSphere(item)); } RecursivePrint(node.Left, logger, indent + 2); RecursivePrint(node.Right, logger, indent + 2); } }
public static BVHNode BuildTopDown(IEnumerable <Sphere> spheres, Action <string> logger, bool print = false) { BVHNode rootNode = new BVHNode(spheres.ToArray()); BuildTopDown(rootNode, logger); if (print) { RecursivePrint(rootNode, logger); } return(rootNode); }
private List <HitPoint> FindHitpoints(Ray ray, BVHNode node) { var hitPoints = new List <HitPoint>(); var queue = new Queue <BVHNode>(); queue.Enqueue(node); while (queue.Count > 0) { var current = queue.Dequeue(); if (current.Items.Length > 0) { foreach (var item in current.Items) { var hitPoint = FindHitpoint(ray, item); if (hitPoint != null) { hitPoints.Add(hitPoint); } } } else { if (current.Left != null) { var boundingSphere = current.Left.BoundingSphere; if (FindHitpoint(ray, boundingSphere) != null) { queue.Enqueue(current.Left); } } if (current.Right != null) { var boundingSphere = current.Right.BoundingSphere; if (FindHitpoint(ray, boundingSphere) != null) { queue.Enqueue(current.Right); } } } } return(hitPoints); }
private bool HasObjectInFrontOfLightSource(Vector3 hitPointVec, Vector3 pos) { var lVec = pos - hitPointVec; var ray = new Ray(hitPointVec, lVec); var length = Vector3.DistanceSquared(ray.StartVec, lVec); if (AccelerationStructure) { BVHNode root = _accelerationStructure; var hitPoints = FindHitpoints(ray, root); foreach (var hitPoint in hitPoints) { var rayVec = hitPoint.Ray.Lambda * hitPoint.Ray.DirectionVec; var scale = Vector3.DistanceSquared(hitPoint.Ray.StartVec, rayVec) / length; if (scale <= 1) { return(true); } } } else { for (int i = 0; i < _spheres.Length; i++) { var sphere = _spheres[i]; var hitPoint = FindHitpoint(ray, sphere); if (hitPoint != null) { var rayVec = hitPoint.Ray.Lambda * hitPoint.Ray.DirectionVec; var scale = Vector3.DistanceSquared(hitPoint.Ray.StartVec, rayVec) / length; if (scale <= 1) { return(true); } } } } return(false); }
private static void BuildTopDown(BVHNode node, Action <string> logger) { int iterations = 0; var queue = new Queue <BVHNode>(); queue.Enqueue(node); BVHNode current = null; while (queue.Count > 0) { ++iterations; current = queue.Dequeue(); if (current.Items.Length > MinPartitionSize) { var boundingSphere = current.BoundingSphere; var splitOrder = GetSplitOrder(boundingSphere.Center); foreach (var split_dim in splitOrder) { var leftSpheres = new List <Sphere>(); var rightSpheres = new List <Sphere>(); var split_coord = 0.5f * ValueAt(boundingSphere.Center, split_dim); foreach (var sphere in current.Items) { if (ValueAt(sphere.Center, split_dim) < split_coord) { leftSpheres.Add(sphere); } else { rightSpheres.Add(sphere); } } var split_success = leftSpheres.Count > 0 && rightSpheres.Count > 0; if (split_success) { current.Left = new BVHNode(leftSpheres.ToArray()); queue.Enqueue(current.Left); current.Right = new BVHNode(rightSpheres.ToArray()); queue.Enqueue(current.Right); current.ClearItems(); break; } } } } logger($"BVH constructed in '{iterations}' iterations."); }
public string GetImageFileName(int width, int height, double dpiX, double dpiY, CancellationToken cancellationToken, string settingsSummary, string exportDirectory) { var sw = Stopwatch.StartNew(); OutputLogEveryXPixel = (width * height / 100); if (AccelerationStructure) { _accelerationStructure = BVHNode.BuildTopDown(_spheres, _logger); } var bitmap = new BitmapImage(width, height, dpiX, dpiY); var divideX = width / (float)2; var alignX = 1 - divideX; var divideY = height / (float)2; var alignY = 1 - divideY; int workDone = 0; int totalWork = width * height; if (Parallelize) { var options = new ParallelOptions() { CancellationToken = cancellationToken, MaxDegreeOfParallelism = Environment.ProcessorCount }; Parallel.For(0, width, options, i => { for (int j = 0; j < height; j++) { if (cancellationToken.IsCancellationRequested) { break; } if (AntiAliasing) { Vector3 result = Vector3.Zero; for (int k = 0; k < AntiAliasingSampleSize; k++) { var dx = (float)_random.NextGaussian(0d, 0.5d); var dy = (float)_random.NextGaussian(0d, 0.5d); var x = (i + alignX + dx) / divideX; var y = ((height - j) + alignY + dy) / divideY; var rgb = GetColor(x, y); result += rgb; } var avg_rgb = result * (1f / AntiAliasingSampleSize); var c = Conversions.FromRGB(avg_rgb, GammaCorrect); bitmap.Set(i, j, c); } else { var x = (i + alignX) / divideX; var y = ((height - j) + alignY) / divideY; var rgb = GetColor(x, y); var c = Conversions.FromRGB(rgb, GammaCorrect); bitmap.Set(i, j, c); } var value = Interlocked.Increment(ref workDone); if (value % OutputLogEveryXPixel == 0) { var progress = (float)value / totalWork; WriteOutput($"{(progress * 100):F3}% progress. Running {sw.Elapsed}. Remaining {TimeSpan.FromMilliseconds(sw.Elapsed.TotalMilliseconds / progress * (1f - progress))}."); } } }); } else { for (int i = 0; i < width; i++) { if (cancellationToken.IsCancellationRequested) { break; } for (int j = 0; j < height; j++) { if (cancellationToken.IsCancellationRequested) { break; } if (AntiAliasing) { Vector3 result = Vector3.Zero; for (int k = 0; k < AntiAliasingSampleSize; k++) { var x = (i + alignX + (float)_random.NextGaussian(0d, 0.5d)) / divideX; var y = ((height - j) + alignY + (float)_random.NextGaussian(0d, 0.5d)) / divideY; var rgb = GetColor(x, y); result += rgb; } var avg_rgb = result * (1f / AntiAliasingSampleSize); var c = Conversions.FromRGB(avg_rgb, GammaCorrect); bitmap.Set(i, j, c); } else { // Question: Why is it mirrored? var x = (i + alignX) / divideX; var y = ((height - j) + alignY) / divideY; var rgb = GetColor(x, y); var c = Conversions.FromRGB(rgb, GammaCorrect); bitmap.Set(i, j, c); } ++workDone; if (workDone % OutputLogEveryXPixel == 0) { var progress = (float)workDone / totalWork; WriteOutput($"{(progress * 100):F3}% progress. Running {sw.Elapsed}. Remaining {TimeSpan.FromMilliseconds(sw.Elapsed.TotalMilliseconds / progress * (1f - progress))}."); } } } } if (cancellationToken.IsCancellationRequested) { WriteOutput("Operation canceled by user."); return(null); } var imageSource = bitmap.GetImageSource(); sw.Stop(); return(SaveImage(imageSource, sw.Elapsed, settingsSummary, exportDirectory)); }