public BvhNode(IEnumerable <IHitable> hitables) { var axis = (int)(3 * Utils.NextFloat()); var sorted = hitables.OrderBy(h => { var box = h.BoundingBox(); return(box.IsOK ? box.Content.Min[axis] : float.NegativeInfinity); }).ToList(); var count = sorted.Count; if (count > 0) { if (count == 1) { Left = Right = sorted[0]; } else { Left = new BvhNode(sorted.Take(count / 2)); Right = new BvhNode(sorted.Skip(count / 2)); } } var leftBox = Left != null?Left.BoundingBox() : new Result <AABB>(); var rightBox = Right != null?Right.BoundingBox() : new Result <AABB>(); if (leftBox.IsOK && rightBox.IsOK) { Box = leftBox.Content + rightBox.Content; } }
private static void Render(SceneDefinition sceneDef, StringBuilder output) { /*var world = new HitableList() * { * new Sphere(new Vec3(0.0f, 0.0f, -1.0f), 0.5f, new Lambertian(new Vec3(0.8f, 0.3f, 0.3f))), * new Sphere(new Vec3(0.0f, -100.5f, -1.0f), 100.0f, new Lambertian(new Vec3(0.8f, 0.8f, 0.0f))), * new Sphere(new Vec3(1.0f, 0.0f, -1.0f), 0.5f, new Metal(new Vec3(0.8f, 0.6f, 0.2f), 1.0f)), * * // make bubble by having a smaller sphere with negative radius * new Sphere(new Vec3(-1.0f, 0.0f, -1.0f), 0.5f, new Dielectric(1.5f)), * new Sphere(new Vec3(-1.0f, 0.0f, -1.0f), -0.45f, new Dielectric(1.5f)), * };*/ var world = new BvhNode(RandomScene()); // var world = TwoPerlinSpheres(); var lookFrom = new Vec3(sceneDef.CameraLookFrom[0], sceneDef.CameraLookFrom[1], sceneDef.CameraLookFrom[2]); var lookAt = new Vec3(sceneDef.CameraLookAt[0], sceneDef.CameraLookAt[1], sceneDef.CameraLookAt[2]); var focusDistance = sceneDef.CameraFocusDistance; var aperture = sceneDef.CameraAperture; var cam = new Camera(lookFrom, lookAt, new Vec3(0.0f, 1.0f, 0.0f), 20.0f, sceneDef.ImageWidth / (float)sceneDef.ImageHeight, aperture, focusDistance); var totalCount = sceneDef.ImageWidth * sceneDef.ImageHeight; var currentCount = 0; var sw = new Stopwatch(); sw.Start(); output.AppendLine($"P3\n{sceneDef.ImageWidth} {sceneDef.ImageHeight}\n255"); if (sceneDef.ChunkSize > 0) { var chunks = Enumerable.Range(0, totalCount).Chunk(sceneDef.ChunkSize * sceneDef.ChunkSize).ToArray(); var bag = new ConcurrentBag <Tuple <int, StringBuilder> >(); var progress = new Progress <int>(cnt => ReportProgress(cnt, totalCount, sw.Elapsed)) as IProgress <int>; Parallel.ForEach(chunks, chunk => { var chunkOutput = new StringBuilder(); foreach (var idx in chunk) { var i = idx % sceneDef.ImageWidth; var j = sceneDef.ImageHeight - 1 - idx / sceneDef.ImageWidth; RenderPart(i, j, sceneDef, cam, world, chunkOutput); Interlocked.Increment(ref currentCount); progress.Report(currentCount); } bag.Add(new Tuple <int, StringBuilder>(chunk.First(), chunkOutput)); }); foreach (var tuple in bag.OrderBy(x => x.Item1)) { output.Append(tuple.Item2); } } else { for (var j = sceneDef.ImageHeight - 1; j >= 0; j--) { for (var i = 0; i < sceneDef.ImageWidth; i++) { RenderPart(i, j, sceneDef, cam, world, output); ReportProgress(++currentCount, totalCount, sw.Elapsed); } } } sw.Stop(); Console.WriteLine($"\nElapsed: {sw.Elapsed.Hours:00}:{sw.Elapsed.Minutes:00}:{sw.Elapsed.Seconds:00}"); }