From 0ef3c1fb819327eac456b4cd8db1c959b4a152a6 Mon Sep 17 00:00:00 2001 From: p-flis Date: Sun, 3 Jan 2021 22:08:34 +0100 Subject: [PATCH 01/16] sphere --- RayTracer/RayTracer.csproj | 24 ++--- RayTracer/Source/BVH/BvhNode.cs | 118 +++++++++++++++++++++++ RayTracer/Source/BVH/HittableList.cs | 50 ++++++++++ RayTracer/Source/Models/Model.cs | 5 + RayTracer/Source/Models/Sphere.cs | 5 + RayTracer/Source/RayTracing/IHittable.cs | 2 +- RayTracer/Source/World/Scene.cs | 10 ++ RayTracerApp/Forms/MainForm.cs | 52 +++++----- 8 files changed, 227 insertions(+), 39 deletions(-) create mode 100644 RayTracer/Source/BVH/BvhNode.cs create mode 100644 RayTracer/Source/BVH/HittableList.cs diff --git a/RayTracer/RayTracer.csproj b/RayTracer/RayTracer.csproj index bd69990..9a90784 100644 --- a/RayTracer/RayTracer.csproj +++ b/RayTracer/RayTracer.csproj @@ -5,20 +5,20 @@ - - - - - - - - + + + + + + + + - - - - + + + + \ No newline at end of file diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs new file mode 100644 index 0000000..568ad84 --- /dev/null +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using RayTracing.Maths; +using RayTracing.RayTracing; + +namespace RayTracing.BVH +{ + public class BvhNode : IHittable + { + public IHittable Left; + public IHittable Right; + public AABB Box; + + public BvhNode() + { + } + + public BvhNode(HittableList list) : this(list.Hittables, 0, list.Hittables.Count) + { + } + + public BvhNode(List srcObjects, int start, int end) + { + var objects = srcObjects; + var random = new Random(); + int axis = random.Next(0, 2); + Func comparator = null; + if (axis == 0) comparator = box_x_compare; + else if (axis == 1) comparator = box_y_compare; + else if (axis == 2) comparator = box_z_compare; + + int object_span = end - start; + if (object_span == 1) + { + Left = Right = objects[start]; + } + else if (object_span == 2) + { + if (comparator(objects[start], objects[start + 1]) < 0) + { + Left = objects[start]; + Right = objects[start + 1]; + } + else + { + Left = objects[start + 1]; + Right = objects[start]; + } + } + else + { + objects.Sort((x, y) => comparator(x, y)); + int mid = start + object_span / 2; + Left = new BvhNode(objects, start, mid); + Right = new BvhNode(objects, mid, end); + } + + AABB boxLeft; + AABB boxRight; + + if (!Left.BoundingBox(out boxLeft) || !Right.BoundingBox(out boxRight)) + { + throw new Exception("No bounding box in constructor"); + } + + Box = HittableList.SurroundingBox(boxLeft, boxRight); + } + + public bool HitTest(Ray ray, ref HitInfo hit, float @from, float to) + { + if (!Box.Test(ref ray, from, to)) + return false; + + bool hitLeft = Left.HitTest(ray, ref hit, from, to); + bool hitRight = Right.HitTest(ray, ref hit, from, hitLeft ? hit.Distance : to); + + return hitLeft || hitRight; + } + + public bool BoundingBox(out AABB outputBox) + { + outputBox = Box; + return true; + } + + public List Preprocess() + { + throw new System.NotImplementedException(); + } + + int box_compare(IHittable a, IHittable b, int axis) + { + AABB box_a; + AABB box_b; + + if (!a.BoundingBox(out box_a) || !b.BoundingBox(out box_b)) + { + int ab = 3; + throw new Exception("No bounding box in bvh_node constructor.\n"); + } + + return box_a.Min[axis].CompareTo(box_b.Min[axis]); + } + + + int box_x_compare (IHittable a, IHittable b) { + return box_compare(a, b, 0); + } + + int box_y_compare (IHittable a, IHittable b) { + return box_compare(a, b, 1); + } + + int box_z_compare (IHittable a, IHittable b) { + return box_compare(a, b, 2); + } + } +} \ No newline at end of file diff --git a/RayTracer/Source/BVH/HittableList.cs b/RayTracer/Source/BVH/HittableList.cs new file mode 100644 index 0000000..8164a43 --- /dev/null +++ b/RayTracer/Source/BVH/HittableList.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using OpenTK; +using RayTracing.Maths; +using RayTracing.RayTracing; + +namespace RayTracing.BVH +{ + public class HittableList : IHittable + { + public List Hittables; + + public bool HitTest(Ray ray, ref HitInfo hit, float @from, float to) + { + throw new System.NotImplementedException(); + } + + public bool BoundingBox(out AABB outputBox) + { + bool firstBox = true; + + outputBox = new AABB(Vector3.One, Vector3.One); + foreach (var hittable in Hittables) + { + if (!hittable.BoundingBox(out var tempBox)) return false; + outputBox = firstBox ? tempBox : SurroundingBox(outputBox, tempBox); + firstBox = false; + } + return true; + } + + public List Preprocess() + { + throw new System.NotImplementedException(); + } + + public static AABB SurroundingBox(AABB box0, AABB box1) + { + var small = new Vector3(Math.Min(box0.Min.X, box1.Min.X), + Math.Min(box0.Min.Y, box1.Min.Y), + Math.Min(box0.Min.Z, box1.Min.Z)); + + var big = new Vector3(Math.Max(box0.Max.X, box1.Max.X), + Math.Max(box0.Max.Y, box1.Max.Y), + Math.Max(box0.Max.Z, box1.Max.Z)); + + return new AABB(small, big); + } + } +} \ No newline at end of file diff --git a/RayTracer/Source/Models/Model.cs b/RayTracer/Source/Models/Model.cs index c5a832b..1f05c04 100644 --- a/RayTracer/Source/Models/Model.cs +++ b/RayTracer/Source/Models/Model.cs @@ -54,6 +54,11 @@ public Model Unload() public abstract bool HitTest(Ray ray, ref HitInfo hit, float from, float to); + public virtual bool BoundingBox(out AABB outputBox) + { + throw new System.NotImplementedException(); + } + public virtual List Preprocess() { return new List {this}; diff --git a/RayTracer/Source/Models/Sphere.cs b/RayTracer/Source/Models/Sphere.cs index 1f68295..101c5d3 100644 --- a/RayTracer/Source/Models/Sphere.cs +++ b/RayTracer/Source/Models/Sphere.cs @@ -63,6 +63,11 @@ public override Mesh GetMesh() return Mesh; } + public override bool BoundingBox(out AABB outputBox) + { + outputBox = new AABB(Position - new Vector3(Scale), Position + new Vector3(Scale)); + return true; + } private (List, List) GetVertexList(short rings, short sectors) { diff --git a/RayTracer/Source/RayTracing/IHittable.cs b/RayTracer/Source/RayTracing/IHittable.cs index 00bc7b0..fe9a445 100644 --- a/RayTracer/Source/RayTracing/IHittable.cs +++ b/RayTracer/Source/RayTracing/IHittable.cs @@ -6,7 +6,7 @@ namespace RayTracing.RayTracing public interface IHittable { bool HitTest(Ray ray, ref HitInfo hit, float from, float to); - + bool BoundingBox(out AABB outputBox); List Preprocess(); } } \ No newline at end of file diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 5a5733f..0762c45 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using RayTracing.BVH; using RayTracing.Lights; using RayTracing.Materials; using RayTracing.Maths; @@ -11,6 +12,7 @@ public class Scene : IHittable { public List Models { get; } = new List(); public List Hittables { get; } = new List(); + public BvhNode Node; public List Lights { get; } = new List(); public AmbientLight AmbientLight { get; set; } @@ -29,6 +31,7 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) bool hitAnything = false; float closest = to; + // return Node.HitTest(ray, ref hit, from, to); foreach (var hittable in Hittables) { if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) @@ -42,6 +45,11 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) return hitAnything; } + public bool BoundingBox(out AABB outputBox) + { + throw new System.NotImplementedException(); + } + //TODO: Might do octree/bvh/w\e division here public List Preprocess() { @@ -50,6 +58,8 @@ public List Preprocess() { Hittables.AddRange(((IHittable)model).Preprocess()); } + + Node = new BvhNode(Hittables, 0, Hittables.Count); return Hittables; } } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index b6c5c5b..ca1c44d 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -73,32 +73,32 @@ private void GLControl_Load(object sender, EventArgs e) Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) }.Load()); - _scene.AddModel(new Cylinder(2) - { - Position = new Vector3(5f, 0.5f, 0), Scale = 1, - Material = new Diffuse(Color.FromColor4(Color4.Chocolate)) - }.Load()); - _scene.AddModel(new Cylinder(2) - { - Position = new Vector3(5f, 0.5f, 4), Scale = 1, - Material = new Diffuse(new Texture("earthmap.jpg")) - }.Load()); - _scene.AddModel(new Cube() - { - Position = new Vector3(0, 0.5f, -3), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f), - }.Load()); - _scene.AddModel(new Rectangle(2) - { - Position = new Vector3(0, 0.5f, -1.99f), Scale = 0.8f, - Material = new Emissive(Color.FromColor4(Color4.White) * 8), - Rotation = new Vector3((float) Math.PI / 2, 0, 0) - }.Load()); - _scene.AddModel(new Plane - { - Position = new Vector3(0, -0.5f, 0), Scale = 1, - Material = new Diffuse(new Texture("wood.jpg")) - }.Load()); + // _scene.AddModel(new Cylinder(2) + // { + // Position = new Vector3(5f, 0.5f, 0), Scale = 1, + // Material = new Diffuse(Color.FromColor4(Color4.Chocolate)) + // }.Load()); + // _scene.AddModel(new Cylinder(2) + // { + // Position = new Vector3(5f, 0.5f, 4), Scale = 1, + // Material = new Diffuse(new Texture("earthmap.jpg")) + // }.Load()); + // _scene.AddModel(new Cube() + // { + // Position = new Vector3(0, 0.5f, -3), Scale = 1, + // Material = new Reflective(new Texture("wood.jpg"), 0.75f), + // }.Load()); + // _scene.AddModel(new Rectangle(2) + // { + // Position = new Vector3(0, 0.5f, -1.99f), Scale = 0.8f, + // Material = new Emissive(Color.FromColor4(Color4.White) * 8), + // Rotation = new Vector3((float) Math.PI / 2, 0, 0) + // }.Load()); + // _scene.AddModel(new Plane + // { + // Position = new Vector3(0, -0.5f, 0), Scale = 1, + // Material = new Diffuse(new Texture("wood.jpg")) + // }.Load()); InitializeFpsTimer(); UpdateViewport(); From 0c0ecb0567aeedacc2bf92e2acd7d7447ceed00a Mon Sep 17 00:00:00 2001 From: p-flis Date: Sun, 3 Jan 2021 22:15:57 +0100 Subject: [PATCH 02/16] triangle --- RayTracer/Source/Models/Triangle.cs | 18 ++++++++++++++++++ RayTracer/Source/World/Scene.cs | 20 ++++++++++---------- RayTracerApp/Forms/MainForm.cs | 7 +++++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index f53edf8..3344e98 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -26,6 +26,24 @@ private protected override void LoadInternal() { throw new Exception("Triangle is used only in ray tracing"); } + + public override bool BoundingBox(out AABB outputBox) + { + var min = _vertices[0]; + var max = _vertices[0]; + foreach (var vertex in _vertices) + { + min.X = Math.Min(min.X, vertex.X); + min.Y = Math.Min(min.Y, vertex.Y); + min.Z = Math.Min(min.Z, vertex.Z); + + max.X = Math.Max(max.X, vertex.X); + max.Y = Math.Max(max.Y, vertex.Y); + max.Z = Math.Max(max.Z, vertex.Z); + } + outputBox = new AABB(min, max); + return true; + } //source: https://www.scratchapixel.com/code.php?id=9&origin=/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle public override bool HitTest(Ray ray, ref HitInfo hit, float @from, float to) diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 0762c45..f09671d 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -31,16 +31,16 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) bool hitAnything = false; float closest = to; - // return Node.HitTest(ray, ref hit, from, to); - foreach (var hittable in Hittables) - { - if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) - { - hitAnything = true; - closest = tempHitInfo.Distance; - hit = tempHitInfo; - } - } + return Node.HitTest(ray, ref hit, from, to); + // foreach (var hittable in Hittables) + // { + // if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) + // { + // hitAnything = true; + // closest = tempHitInfo.Distance; + // hit = tempHitInfo; + // } + // } return hitAnything; } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index ca1c44d..0b4452d 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -73,6 +73,13 @@ private void GLControl_Load(object sender, EventArgs e) Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) }.Load()); + + _scene.AddModel(new Triangle( + new Vector3(-4f, 0f, -1f), + new Vector3(-5f, 2f, 2.5f), + new Vector3(-5f, -0.5f, 2f) + ) {Material = new Reflective(Color.FromColor4(Color4.AliceBlue), 0.05f)}); + // _scene.AddModel(new Cylinder(2) // { // Position = new Vector3(5f, 0.5f, 0), Scale = 1, From 3761ef4acafb676977a21871abdaaba9af38f858 Mon Sep 17 00:00:00 2001 From: p-flis Date: Sun, 3 Jan 2021 23:05:50 +0100 Subject: [PATCH 03/16] cylinder --- RayTracer/Source/Models/Cylinder.cs | 14 ++++++++++++++ RayTracerApp/Forms/MainForm.cs | 20 ++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/RayTracer/Source/Models/Cylinder.cs b/RayTracer/Source/Models/Cylinder.cs index c9f2734..1a15889 100644 --- a/RayTracer/Source/Models/Cylinder.cs +++ b/RayTracer/Source/Models/Cylinder.cs @@ -147,6 +147,20 @@ private void CapHitTest(ref Ray ray, ref HitInfo hit, float from, float to, ref } } + // https://www.iquilezles.org/www/articles/diskbbox/diskbbox.htm + public override bool BoundingBox(out AABB outputBox) + { + var normal = _normal.Normalized(); + float x = (float) Math.Sqrt(1 - normal.X * normal.X); + float y = (float) Math.Sqrt(1 - normal.Y * normal.Y); + float z = (float) Math.Sqrt(1 - normal.Z * normal.Z); + Vector3 e = new Vector3(x, y, z) * Scale; + outputBox = new AABB( + Vector3.ComponentMin(_top - e, _bottom - e), + Vector3.ComponentMax(_top + e, _bottom + e)); + return true; + } + private void GetCylinderUV(Vector3 hitPoint, ref Vector2 uv) { var hitVector = RotationMatrix * (hitPoint - Position) / _height; diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index 0b4452d..2747892 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -80,16 +80,16 @@ private void GLControl_Load(object sender, EventArgs e) new Vector3(-5f, -0.5f, 2f) ) {Material = new Reflective(Color.FromColor4(Color4.AliceBlue), 0.05f)}); - // _scene.AddModel(new Cylinder(2) - // { - // Position = new Vector3(5f, 0.5f, 0), Scale = 1, - // Material = new Diffuse(Color.FromColor4(Color4.Chocolate)) - // }.Load()); - // _scene.AddModel(new Cylinder(2) - // { - // Position = new Vector3(5f, 0.5f, 4), Scale = 1, - // Material = new Diffuse(new Texture("earthmap.jpg")) - // }.Load()); + _scene.AddModel(new Cylinder(2) + { + Position = new Vector3(5f, 0.5f, 0), Scale = 1, + Material = new Diffuse(Color.FromColor4(Color4.Chocolate)) + }.Load()); + _scene.AddModel(new Cylinder(2) + { + Position = new Vector3(5f, 0.5f, 4), Scale = 1, + Material = new Diffuse(new Texture("earthmap.jpg")) + }.Load()); // _scene.AddModel(new Cube() // { // Position = new Vector3(0, 0.5f, -3), Scale = 1, From 383533081afb4fdbdd15447f0ba3f5b3a310a414 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 00:09:36 +0100 Subject: [PATCH 04/16] small refactoring + bvh bugfix --- RayTracer/Source/BVH/BvhNode.cs | 19 ++++++++++-- RayTracer/Source/BVH/HittableList.cs | 12 +++++--- RayTracer/Source/Models/Triangle.cs | 17 +++-------- RayTracerApp/Forms/MainForm.cs | 43 ++++++++++++++-------------- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 568ad84..411ffec 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -49,7 +49,7 @@ public BvhNode(List srcObjects, int start, int end) } else { - objects.Sort((x, y) => comparator(x, y)); + objects.Sort(start, object_span-1, new FuncComparer(comparator)); int mid = start + object_span / 2; Left = new BvhNode(objects, start, mid); Right = new BvhNode(objects, mid, end); @@ -87,7 +87,20 @@ public List Preprocess() { throw new System.NotImplementedException(); } - + + private class FuncComparer : IComparer + { + private readonly Func func; + public FuncComparer(Func comparerFunc) + { + func = comparerFunc; + } + + public int Compare(T x, T y) + { + return func(x, y); + } + } int box_compare(IHittable a, IHittable b, int axis) { AABB box_a; @@ -95,10 +108,10 @@ int box_compare(IHittable a, IHittable b, int axis) if (!a.BoundingBox(out box_a) || !b.BoundingBox(out box_b)) { - int ab = 3; throw new Exception("No bounding box in bvh_node constructor.\n"); } + return box_a.Min[axis].CompareTo(box_b.Min[axis]); } diff --git a/RayTracer/Source/BVH/HittableList.cs b/RayTracer/Source/BVH/HittableList.cs index 8164a43..2b238ed 100644 --- a/RayTracer/Source/BVH/HittableList.cs +++ b/RayTracer/Source/BVH/HittableList.cs @@ -36,13 +36,17 @@ public List Preprocess() public static AABB SurroundingBox(AABB box0, AABB box1) { - var small = new Vector3(Math.Min(box0.Min.X, box1.Min.X), + var small = new Vector3( + Math.Min(box0.Min.X, box1.Min.X), Math.Min(box0.Min.Y, box1.Min.Y), - Math.Min(box0.Min.Z, box1.Min.Z)); + Math.Min(box0.Min.Z, box1.Min.Z) + ); - var big = new Vector3(Math.Max(box0.Max.X, box1.Max.X), + var big = new Vector3( + Math.Max(box0.Max.X, box1.Max.X), Math.Max(box0.Max.Y, box1.Max.Y), - Math.Max(box0.Max.Z, box1.Max.Z)); + Math.Max(box0.Max.Z, box1.Max.Z) + ); return new AABB(small, big); } diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index 3344e98..4103d9e 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -29,19 +29,10 @@ private protected override void LoadInternal() public override bool BoundingBox(out AABB outputBox) { - var min = _vertices[0]; - var max = _vertices[0]; - foreach (var vertex in _vertices) - { - min.X = Math.Min(min.X, vertex.X); - min.Y = Math.Min(min.Y, vertex.Y); - min.Z = Math.Min(min.Z, vertex.Z); - - max.X = Math.Max(max.X, vertex.X); - max.Y = Math.Max(max.Y, vertex.Y); - max.Z = Math.Max(max.Z, vertex.Z); - } - outputBox = new AABB(min, max); + outputBox = new AABB( + Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]),_vertices[2]), + Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]),_vertices[2]) + ); return true; } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index 2747892..b42661c 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -58,22 +58,22 @@ private void GLControl_Load(object sender, EventArgs e) _rayTracer = new IncrementalRayTracer(10, 64 * 64, Vec2Sampling.Jittered, gLControl.Width, 50); _cameraController = new CameraController(_camera, gLControl, UpdateLastModification); _scene.AmbientLight = new AmbientLight {Color = Color.FromColor4(Color4.LightSkyBlue)}; - _scene.AddModel(new Sphere - { - Position = new Vector3(0, 5.5f, 0), Scale = 1, - Material = new Emissive(Color.FromColor4(Color4.White)) - }.Load()); - _scene.AddModel(new Sphere - { - Position = new Vector3(-2.5f, 0.5f, 1), Scale = 1, - Material = new Reflective(Color.FromColor4(Color4.Azure), 0.1f) - }.Load()); - _scene.AddModel(new Sphere - { - Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, - Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) - }.Load()); - + // _scene.AddModel(new Sphere + // { + // Position = new Vector3(0, 5.5f, 0), Scale = 1, + // Material = new Emissive(Color.FromColor4(Color4.White)) + // }.Load()); + // _scene.AddModel(new Sphere + // { + // Position = new Vector3(-2.5f, 0.5f, 1), Scale = 1, + // Material = new Reflective(Color.FromColor4(Color4.Azure), 0.1f) + // }.Load()); + // _scene.AddModel(new Sphere + // { + // Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, + // Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) + // }.Load()); + // _scene.AddModel(new Triangle( new Vector3(-4f, 0f, -1f), new Vector3(-5f, 2f, 2.5f), @@ -90,11 +90,12 @@ private void GLControl_Load(object sender, EventArgs e) Position = new Vector3(5f, 0.5f, 4), Scale = 1, Material = new Diffuse(new Texture("earthmap.jpg")) }.Load()); - // _scene.AddModel(new Cube() - // { - // Position = new Vector3(0, 0.5f, -3), Scale = 1, - // Material = new Reflective(new Texture("wood.jpg"), 0.75f), - // }.Load()); + _scene.AddModel(new Cube() + { + Position = new Vector3(0, 0.5f, -3), Scale = 1, + Material = new Reflective(new Texture("wood.jpg"), 0.75f), + Rotation = Vector3.One * (float)Math.PI/6 + }.Load()); // _scene.AddModel(new Rectangle(2) // { // Position = new Vector3(0, 0.5f, -1.99f), Scale = 0.8f, From 67c226271d507d2fc1374175e7d5a931691af2ae Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 00:29:52 +0100 Subject: [PATCH 05/16] plane handling --- RayTracer/Source/World/Scene.cs | 34 ++++++++++++++++---------- RayTracerApp/Forms/MainForm.cs | 42 ++++++++++++++++----------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index f09671d..588a5fb 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -12,6 +12,7 @@ public class Scene : IHittable { public List Models { get; } = new List(); public List Hittables { get; } = new List(); + public List BetterHittables = new List(); public BvhNode Node; public List Lights { get; } = new List(); public AmbientLight AmbientLight { get; set; } @@ -31,17 +32,17 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) bool hitAnything = false; float closest = to; - return Node.HitTest(ray, ref hit, from, to); - // foreach (var hittable in Hittables) - // { - // if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) - // { - // hitAnything = true; - // closest = tempHitInfo.Distance; - // hit = tempHitInfo; - // } - // } - + //return Node.HitTest(ray, ref hit, from, to); + foreach (var hittable in BetterHittables) + { + if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) + { + hitAnything = true; + closest = tempHitInfo.Distance; + hit = tempHitInfo; + } + } + return hitAnything; } @@ -54,12 +55,21 @@ public bool BoundingBox(out AABB outputBox) public List Preprocess() { Hittables.Clear(); + BetterHittables.Clear(); foreach (var model in Models) { - Hittables.AddRange(((IHittable)model).Preprocess()); + if (model is Plane) + { + BetterHittables.Add(model); + } + else + { + Hittables.AddRange(((IHittable)model).Preprocess()); + } } Node = new BvhNode(Hittables, 0, Hittables.Count); + BetterHittables.Add(Node); return Hittables; } } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index b42661c..89cebcc 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -58,22 +58,22 @@ private void GLControl_Load(object sender, EventArgs e) _rayTracer = new IncrementalRayTracer(10, 64 * 64, Vec2Sampling.Jittered, gLControl.Width, 50); _cameraController = new CameraController(_camera, gLControl, UpdateLastModification); _scene.AmbientLight = new AmbientLight {Color = Color.FromColor4(Color4.LightSkyBlue)}; - // _scene.AddModel(new Sphere - // { - // Position = new Vector3(0, 5.5f, 0), Scale = 1, - // Material = new Emissive(Color.FromColor4(Color4.White)) - // }.Load()); - // _scene.AddModel(new Sphere - // { - // Position = new Vector3(-2.5f, 0.5f, 1), Scale = 1, - // Material = new Reflective(Color.FromColor4(Color4.Azure), 0.1f) - // }.Load()); - // _scene.AddModel(new Sphere - // { - // Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, - // Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) - // }.Load()); - // + _scene.AddModel(new Sphere + { + Position = new Vector3(0, 5.5f, 0), Scale = 1, + Material = new Emissive(Color.FromColor4(Color4.White)) + }.Load()); + _scene.AddModel(new Sphere + { + Position = new Vector3(-2.5f, 0.5f, 1), Scale = 1, + Material = new Reflective(Color.FromColor4(Color4.Azure), 0.1f) + }.Load()); + _scene.AddModel(new Sphere + { + Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, + Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) + }.Load()); + _scene.AddModel(new Triangle( new Vector3(-4f, 0f, -1f), new Vector3(-5f, 2f, 2.5f), @@ -102,11 +102,11 @@ private void GLControl_Load(object sender, EventArgs e) // Material = new Emissive(Color.FromColor4(Color4.White) * 8), // Rotation = new Vector3((float) Math.PI / 2, 0, 0) // }.Load()); - // _scene.AddModel(new Plane - // { - // Position = new Vector3(0, -0.5f, 0), Scale = 1, - // Material = new Diffuse(new Texture("wood.jpg")) - // }.Load()); + _scene.AddModel(new Plane + { + Position = new Vector3(0, -0.5f, 0), Scale = 1, + Material = new Diffuse(new Texture("wood.jpg")) + }.Load()); InitializeFpsTimer(); UpdateViewport(); From 55eb3cb83538a7b733e7c083df5bcd29f9ac79d7 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 01:15:39 +0100 Subject: [PATCH 06/16] triangle bounding box fix --- RayTracer/Source/Models/Triangle.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index 4103d9e..2d88e37 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -33,6 +33,19 @@ public override bool BoundingBox(out AABB outputBox) Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]),_vertices[2]), Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]),_vertices[2]) ); + for (int i = 0; i < 3; i++) + { + if(Math.Abs(outputBox.Max[i] - outputBox.Min[i]) < 1e-5) + { + var vec = outputBox.Min; + vec[i] -= 0.001f; + outputBox.Min = vec; + + vec = outputBox.Max; + vec[i] += 0.001f; + outputBox.Max = vec; + } + } return true; } From 161f282fd494f803d60bb60d285141e6705d9816 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 13:19:02 +0100 Subject: [PATCH 07/16] cleanup --- RayTracer/Source/BVH/BvhNode.cs | 106 ++++++++++----------------- RayTracer/Source/BVH/HittableList.cs | 54 -------------- RayTracer/Source/Maths/AABB.cs | 16 ++++ RayTracer/Source/Models/Triangle.cs | 8 +- RayTracer/Source/World/Scene.cs | 24 +++--- RayTracerApp/Forms/MainForm.cs | 30 ++++++-- 6 files changed, 94 insertions(+), 144 deletions(-) delete mode 100644 RayTracer/Source/BVH/HittableList.cs diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 411ffec..a4476d1 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -7,79 +7,66 @@ namespace RayTracing.BVH { public class BvhNode : IHittable { - public IHittable Left; - public IHittable Right; - public AABB Box; - - public BvhNode() - { - } - - public BvhNode(HittableList list) : this(list.Hittables, 0, list.Hittables.Count) - { - } + private readonly IHittable _left; + private readonly IHittable _right; + private readonly AABB _box; + // https://raytracing.github.io/books/RayTracingTheNextWeek.html#boundingvolumehierarchies public BvhNode(List srcObjects, int start, int end) { var objects = srcObjects; var random = new Random(); int axis = random.Next(0, 2); - Func comparator = null; - if (axis == 0) comparator = box_x_compare; - else if (axis == 1) comparator = box_y_compare; - else if (axis == 2) comparator = box_z_compare; + int Comparator(IHittable a, IHittable b) => BoxCompare(a, b, axis); - int object_span = end - start; - if (object_span == 1) + int objectSpan = end - start; + if (objectSpan == 1) { - Left = Right = objects[start]; + _left = _right = objects[start]; } - else if (object_span == 2) + else if (objectSpan == 2) { - if (comparator(objects[start], objects[start + 1]) < 0) + if (Comparator(objects[start], objects[start + 1]) < 0) { - Left = objects[start]; - Right = objects[start + 1]; + _left = objects[start]; + _right = objects[start + 1]; } else { - Left = objects[start + 1]; - Right = objects[start]; + _left = objects[start + 1]; + _right = objects[start]; } } else { - objects.Sort(start, object_span-1, new FuncComparer(comparator)); - int mid = start + object_span / 2; - Left = new BvhNode(objects, start, mid); - Right = new BvhNode(objects, mid, end); + objects.Sort(start, objectSpan - 1, new FuncComparer(Comparator)); + int mid = start + objectSpan / 2; + _left = new BvhNode(objects, start, mid); + _right = new BvhNode(objects, mid, end); } - AABB boxLeft; - AABB boxRight; - - if (!Left.BoundingBox(out boxLeft) || !Right.BoundingBox(out boxRight)) + if (!_left.BoundingBox(out var boxLeft) || !_right.BoundingBox(out var boxRight)) { - throw new Exception("No bounding box in constructor"); + throw new Exception("No bounding box"); } - Box = HittableList.SurroundingBox(boxLeft, boxRight); + _box = AABB.SurroundingBox(boxLeft, boxRight); } public bool HitTest(Ray ray, ref HitInfo hit, float @from, float to) { - if (!Box.Test(ref ray, from, to)) + if (!_box.Test(ref ray, from, to)) return false; - bool hitLeft = Left.HitTest(ray, ref hit, from, to); - bool hitRight = Right.HitTest(ray, ref hit, from, hitLeft ? hit.Distance : to); + bool hitLeft = _left.HitTest(ray, ref hit, from, to); + bool hitRight = _right.HitTest(ray, ref hit, from, hitLeft ? hit.Distance : to); return hitLeft || hitRight; } public bool BoundingBox(out AABB outputBox) { - outputBox = Box; + outputBox = _box; return true; } @@ -88,44 +75,31 @@ public List Preprocess() throw new System.NotImplementedException(); } - private class FuncComparer : IComparer - { - private readonly Func func; - public FuncComparer(Func comparerFunc) - { - func = comparerFunc; - } - - public int Compare(T x, T y) - { - return func(x, y); - } - } - int box_compare(IHittable a, IHittable b, int axis) + private int BoxCompare(IHittable a, IHittable b, int axis) { - AABB box_a; - AABB box_b; - - if (!a.BoundingBox(out box_a) || !b.BoundingBox(out box_b)) + if (!a.BoundingBox(out var boxA) || !b.BoundingBox(out var boxB)) { throw new Exception("No bounding box in bvh_node constructor.\n"); } - - return box_a.Min[axis].CompareTo(box_b.Min[axis]); + + return boxA.Min[axis].CompareTo(boxB.Min[axis]); } - int box_x_compare (IHittable a, IHittable b) { - return box_compare(a, b, 0); - } + private class FuncComparer : IComparer + { + private readonly Func _func; - int box_y_compare (IHittable a, IHittable b) { - return box_compare(a, b, 1); - } + public FuncComparer(Func comparerFunc) + { + _func = comparerFunc; + } - int box_z_compare (IHittable a, IHittable b) { - return box_compare(a, b, 2); + public int Compare(T x, T y) + { + return _func(x, y); + } } } } \ No newline at end of file diff --git a/RayTracer/Source/BVH/HittableList.cs b/RayTracer/Source/BVH/HittableList.cs deleted file mode 100644 index 2b238ed..0000000 --- a/RayTracer/Source/BVH/HittableList.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using OpenTK; -using RayTracing.Maths; -using RayTracing.RayTracing; - -namespace RayTracing.BVH -{ - public class HittableList : IHittable - { - public List Hittables; - - public bool HitTest(Ray ray, ref HitInfo hit, float @from, float to) - { - throw new System.NotImplementedException(); - } - - public bool BoundingBox(out AABB outputBox) - { - bool firstBox = true; - - outputBox = new AABB(Vector3.One, Vector3.One); - foreach (var hittable in Hittables) - { - if (!hittable.BoundingBox(out var tempBox)) return false; - outputBox = firstBox ? tempBox : SurroundingBox(outputBox, tempBox); - firstBox = false; - } - return true; - } - - public List Preprocess() - { - throw new System.NotImplementedException(); - } - - public static AABB SurroundingBox(AABB box0, AABB box1) - { - var small = new Vector3( - Math.Min(box0.Min.X, box1.Min.X), - Math.Min(box0.Min.Y, box1.Min.Y), - Math.Min(box0.Min.Z, box1.Min.Z) - ); - - var big = new Vector3( - Math.Max(box0.Max.X, box1.Max.X), - Math.Max(box0.Max.Y, box1.Max.Y), - Math.Max(box0.Max.Z, box1.Max.Z) - ); - - return new AABB(small, big); - } - } -} \ No newline at end of file diff --git a/RayTracer/Source/Maths/AABB.cs b/RayTracer/Source/Maths/AABB.cs index 3e7ab22..097a62a 100644 --- a/RayTracer/Source/Maths/AABB.cs +++ b/RayTracer/Source/Maths/AABB.cs @@ -47,5 +47,21 @@ public bool Test(ref Ray ray, float from = 0, float to = float.PositiveInfinity) Vector3.ComponentMax(first.Max, second.Max)); } + public static AABB SurroundingBox(AABB box0, AABB box1) + { + var small = new Vector3( + Math.Min(box0.Min.X, box1.Min.X), + Math.Min(box0.Min.Y, box1.Min.Y), + Math.Min(box0.Min.Z, box1.Min.Z) + ); + + var big = new Vector3( + Math.Max(box0.Max.X, box1.Max.X), + Math.Max(box0.Max.Y, box1.Max.Y), + Math.Max(box0.Max.Z, box1.Max.Z) + ); + + return new AABB(small, big); + } } } \ No newline at end of file diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index 2d88e37..5c486d9 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -33,16 +33,18 @@ public override bool BoundingBox(out AABB outputBox) Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]),_vertices[2]), Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]),_vertices[2]) ); + const float thickness = (float) 1e-5; + const float threshold = (float) 1e-7; for (int i = 0; i < 3; i++) { - if(Math.Abs(outputBox.Max[i] - outputBox.Min[i]) < 1e-5) + if(Math.Abs(outputBox.Max[i] - outputBox.Min[i]) < threshold) { var vec = outputBox.Min; - vec[i] -= 0.001f; + vec[i] -= thickness; outputBox.Min = vec; vec = outputBox.Max; - vec[i] += 0.001f; + vec[i] += thickness; outputBox.Max = vec; } } diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 588a5fb..37b51bb 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -12,15 +12,13 @@ public class Scene : IHittable { public List Models { get; } = new List(); public List Hittables { get; } = new List(); - public List BetterHittables = new List(); - public BvhNode Node; public List Lights { get; } = new List(); public AmbientLight AmbientLight { get; set; } public void AddModel(Model model) { Models.Add(model); - if (model.Material is Emissive) // add MasterMaterial + if (model.Material is Emissive) { Lights.Add(model); } @@ -31,9 +29,8 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) HitInfo tempHitInfo = new HitInfo(); bool hitAnything = false; float closest = to; - - //return Node.HitTest(ray, ref hit, from, to); - foreach (var hittable in BetterHittables) + + foreach (var hittable in Hittables) { if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) { @@ -50,26 +47,27 @@ public bool BoundingBox(out AABB outputBox) { throw new System.NotImplementedException(); } - - //TODO: Might do octree/bvh/w\e division here + public List Preprocess() { Hittables.Clear(); - BetterHittables.Clear(); + var planes = new List(); + var hittablesToBvh = new List(); foreach (var model in Models) { if (model is Plane) { - BetterHittables.Add(model); + planes.Add(model); } else { - Hittables.AddRange(((IHittable)model).Preprocess()); + hittablesToBvh.AddRange(((IHittable)model).Preprocess()); } } - Node = new BvhNode(Hittables, 0, Hittables.Count); - BetterHittables.Add(Node); + var node = new BvhNode(hittablesToBvh, 0, hittablesToBvh.Count); + Hittables.AddRange(planes); + Hittables.Add(node); return Hittables; } } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index 89cebcc..888f822 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -93,15 +93,29 @@ private void GLControl_Load(object sender, EventArgs e) _scene.AddModel(new Cube() { Position = new Vector3(0, 0.5f, -3), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f), - Rotation = Vector3.One * (float)Math.PI/6 + Material = new Reflective(new Texture("wood.jpg"), 0.75f) + }.Load()); + _scene.AddModel(new Cube() + { + Position = new Vector3(0, 4f, -3), Scale = 1, + Material = new Reflective(new Texture("wood.jpg"), 0.75f) + }.Load()); + _scene.AddModel(new Cube() + { + Position = new Vector3(0, 8f, -3), Scale = 1, + Material = new Reflective(new Texture("wood.jpg"), 0.75f) + }.Load()); + _scene.AddModel(new Cube() + { + Position = new Vector3(0, 12f, -4), Scale = 1, + Material = new Reflective(new Texture("wood.jpg"), 0.75f) + }.Load()); + _scene.AddModel(new Rectangle(2) + { + Position = new Vector3(0, 0.5f, -1.99f), Scale = 0.8f, + Material = new Emissive(Color.FromColor4(Color4.White) * 8), + Rotation = new Vector3((float) Math.PI / 2, 0, 0) }.Load()); - // _scene.AddModel(new Rectangle(2) - // { - // Position = new Vector3(0, 0.5f, -1.99f), Scale = 0.8f, - // Material = new Emissive(Color.FromColor4(Color4.White) * 8), - // Rotation = new Vector3((float) Math.PI / 2, 0, 0) - // }.Load()); _scene.AddModel(new Plane { Position = new Vector3(0, -0.5f, 0), Scale = 1, From 35dcb53ebc42a09069570bfba3ee1237468a5632 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 13:20:13 +0100 Subject: [PATCH 08/16] exception message --- RayTracer/Source/BVH/BvhNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index a4476d1..19c2d4d 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -79,7 +79,7 @@ private int BoxCompare(IHittable a, IHittable b, int axis) { if (!a.BoundingBox(out var boxA) || !b.BoundingBox(out var boxB)) { - throw new Exception("No bounding box in bvh_node constructor.\n"); + throw new Exception("No bounding box.\n"); } From ec131b251edc00060da3e452745d93e0861b3191 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 13:33:54 +0100 Subject: [PATCH 09/16] random 3 axis --- RayTracer/Source/BVH/BvhNode.cs | 7 +++---- RayTracer/Source/World/Scene.cs | 6 +++--- RayTracerApp/Forms/MainForm.cs | 24 +----------------------- 3 files changed, 7 insertions(+), 30 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 19c2d4d..e0c0654 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using RayTracing.Maths; using RayTracing.RayTracing; +using RayTracing.Sampling; namespace RayTracing.BVH { @@ -16,7 +17,7 @@ public BvhNode(List srcObjects, int start, int end) { var objects = srcObjects; var random = new Random(); - int axis = random.Next(0, 2); + int axis = random.Next(0, 3); int Comparator(IHittable a, IHittable b) => BoxCompare(a, b, axis); int objectSpan = end - start; @@ -79,14 +80,12 @@ private int BoxCompare(IHittable a, IHittable b, int axis) { if (!a.BoundingBox(out var boxA) || !b.BoundingBox(out var boxB)) { - throw new Exception("No bounding box.\n"); + throw new Exception("No bounding box"); } - return boxA.Min[axis].CompareTo(boxB.Min[axis]); } - private class FuncComparer : IComparer { private readonly Func _func; diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 37b51bb..a4ffc09 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -18,7 +18,7 @@ public class Scene : IHittable public void AddModel(Model model) { Models.Add(model); - if (model.Material is Emissive) + if (model.Material is Emissive) // add MasterMaterial { Lights.Add(model); } @@ -29,7 +29,7 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) HitInfo tempHitInfo = new HitInfo(); bool hitAnything = false; float closest = to; - + foreach (var hittable in Hittables) { if (hittable.HitTest(ray, ref tempHitInfo, from, closest)) @@ -39,7 +39,7 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) hit = tempHitInfo; } } - + return hitAnything; } diff --git a/RayTracerApp/Forms/MainForm.cs b/RayTracerApp/Forms/MainForm.cs index 888f822..b6c5c5b 100644 --- a/RayTracerApp/Forms/MainForm.cs +++ b/RayTracerApp/Forms/MainForm.cs @@ -73,13 +73,6 @@ private void GLControl_Load(object sender, EventArgs e) Position = new Vector3(2.5f, 0.5f, 1), Scale = 1, Material = new Reflective(new Texture("earthmap.jpg"), 0.75f) }.Load()); - - _scene.AddModel(new Triangle( - new Vector3(-4f, 0f, -1f), - new Vector3(-5f, 2f, 2.5f), - new Vector3(-5f, -0.5f, 2f) - ) {Material = new Reflective(Color.FromColor4(Color4.AliceBlue), 0.05f)}); - _scene.AddModel(new Cylinder(2) { Position = new Vector3(5f, 0.5f, 0), Scale = 1, @@ -93,22 +86,7 @@ private void GLControl_Load(object sender, EventArgs e) _scene.AddModel(new Cube() { Position = new Vector3(0, 0.5f, -3), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f) - }.Load()); - _scene.AddModel(new Cube() - { - Position = new Vector3(0, 4f, -3), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f) - }.Load()); - _scene.AddModel(new Cube() - { - Position = new Vector3(0, 8f, -3), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f) - }.Load()); - _scene.AddModel(new Cube() - { - Position = new Vector3(0, 12f, -4), Scale = 1, - Material = new Reflective(new Texture("wood.jpg"), 0.75f) + Material = new Reflective(new Texture("wood.jpg"), 0.75f), }.Load()); _scene.AddModel(new Rectangle(2) { From 8aa47e1f6dd57cb41fc6d78e17c50ec9f4a05ce1 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 13:55:14 +0100 Subject: [PATCH 10/16] axis sampler instead of Random --- RayTracer/Source/BVH/BvhNode.cs | 8 ++++---- RayTracer/Source/RayTracing/IHittable.cs | 2 +- RayTracer/Source/Sampling/IntSampling.cs | 17 +++++++++++++++++ RayTracer/Source/World/Scene.cs | 15 ++++++++++++--- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index e0c0654..90f6901 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -13,11 +13,11 @@ public class BvhNode : IHittable private readonly AABB _box; // https://raytracing.github.io/books/RayTracingTheNextWeek.html#boundingvolumehierarchies - public BvhNode(List srcObjects, int start, int end) + public BvhNode(List srcObjects, int start, int end, AbstractSampler sampler) { var objects = srcObjects; var random = new Random(); - int axis = random.Next(0, 3); + int axis = sampler.GetSample(); int Comparator(IHittable a, IHittable b) => BoxCompare(a, b, axis); int objectSpan = end - start; @@ -42,8 +42,8 @@ public BvhNode(List srcObjects, int start, int end) { objects.Sort(start, objectSpan - 1, new FuncComparer(Comparator)); int mid = start + objectSpan / 2; - _left = new BvhNode(objects, start, mid); - _right = new BvhNode(objects, mid, end); + _left = new BvhNode(objects, start, mid, sampler); + _right = new BvhNode(objects, mid, end, sampler); } if (!_left.BoundingBox(out var boxLeft) || !_right.BoundingBox(out var boxRight)) diff --git a/RayTracer/Source/RayTracing/IHittable.cs b/RayTracer/Source/RayTracing/IHittable.cs index fe9a445..7bab75a 100644 --- a/RayTracer/Source/RayTracing/IHittable.cs +++ b/RayTracer/Source/RayTracing/IHittable.cs @@ -6,7 +6,7 @@ namespace RayTracing.RayTracing public interface IHittable { bool HitTest(Ray ray, ref HitInfo hit, float from, float to); - bool BoundingBox(out AABB outputBox); List Preprocess(); + bool BoundingBox(out AABB outputBox); } } \ No newline at end of file diff --git a/RayTracer/Source/Sampling/IntSampling.cs b/RayTracer/Source/Sampling/IntSampling.cs index 4b25fe5..ee8c112 100644 --- a/RayTracer/Source/Sampling/IntSampling.cs +++ b/RayTracer/Source/Sampling/IntSampling.cs @@ -42,5 +42,22 @@ public static List Distribution(int count, params float[] probabilities) return samples; } + + public static List Random(int count, int minInclusive, int maxExclusive) + { + if(count < 0) + throw new ArgumentException("Count less than zero"); + if (minInclusive >= maxExclusive) + throw new ArgumentException("Wrong limits"); + + List samples = new List(); + + for (int i = 0; i < count; i++) + { + samples.Add(_random.Next(minInclusive, maxExclusive)); + } + + return samples; + } } } \ No newline at end of file diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index ee1b512..db24e1d 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -5,16 +5,25 @@ using RayTracing.Maths; using RayTracing.Models; using RayTracing.RayTracing; +using RayTracing.Sampling; namespace RayTracing.World { public class Scene : IHittable { + private const int SAMPLES = 10000; public List Models { get; } = new List(); public List Hittables { get; } = new List(); public List Lights { get; } = new List(); public AmbientLight AmbientLight { get; set; } + private readonly AbstractSampler _axisSampler; + + public Scene() + { + _axisSampler = new ThreadSafeSampler(count => IntSampling.Random(count, 0, 3), SAMPLES); + } + public void AddModel(Model model) { Models.Add(model); @@ -48,7 +57,7 @@ public bool BoundingBox(out AABB outputBox) { throw new System.NotImplementedException(); } - + public List Preprocess() { Hittables.Clear(); @@ -62,11 +71,11 @@ public List Preprocess() } else { - hittablesToBvh.AddRange(((IHittable)model).Preprocess()); + hittablesToBvh.AddRange(((IHittable) model).Preprocess()); } } - var node = new BvhNode(hittablesToBvh, 0, hittablesToBvh.Count); + var node = new BvhNode(hittablesToBvh, 0, hittablesToBvh.Count, _axisSampler); Hittables.AddRange(planes); Hittables.Add(node); return Hittables; From 515d682bfac6fa84461dbf102e91ae91313da386 Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 14:08:32 +0100 Subject: [PATCH 11/16] BvhMode switch --- RayTracer/Source/World/Scene.cs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index db24e1d..b6b89fe 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -12,6 +12,7 @@ namespace RayTracing.World public class Scene : IHittable { private const int SAMPLES = 10000; + public bool BvhMode { get; set; } = true; public List Models { get; } = new List(); public List Hittables { get; } = new List(); public List Lights { get; } = new List(); @@ -58,7 +59,17 @@ public bool BoundingBox(out AABB outputBox) throw new System.NotImplementedException(); } - public List Preprocess() + private List StandardPreprocess() + { + Hittables.Clear(); + foreach (var model in Models) + { + Hittables.AddRange(((IHittable) model).Preprocess()); + } + return Hittables; + } + + private List BvhPreprocess() { Hittables.Clear(); var planes = new List(); @@ -80,5 +91,17 @@ public List Preprocess() Hittables.Add(node); return Hittables; } + + public List Preprocess() + { + if (BvhMode) + { + return BvhPreprocess(); + } + else + { + return StandardPreprocess(); + } + } } } \ No newline at end of file From 86efbe4293f2996cbfb63556a33108b2fef64cdf Mon Sep 17 00:00:00 2001 From: p-flis Date: Mon, 4 Jan 2021 20:14:42 +0100 Subject: [PATCH 12/16] better surrounding box --- RayTracer/Source/BVH/BvhNode.cs | 2 +- RayTracer/Source/Maths/AABB.cs | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 90f6901..870eea2 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -51,7 +51,7 @@ public BvhNode(List srcObjects, int start, int end, AbstractSampler Date: Tue, 5 Jan 2021 00:30:36 +0100 Subject: [PATCH 13/16] bounding box function refactorization --- RayTracer/Source/BVH/BvhNode.cs | 17 ++++++----------- RayTracer/Source/Models/Cylinder.cs | 5 ++--- RayTracer/Source/Models/Model.cs | 2 +- RayTracer/Source/Models/Sphere.cs | 5 ++--- RayTracer/Source/Models/Triangle.cs | 7 ++++--- RayTracer/Source/RayTracing/IHittable.cs | 2 +- RayTracer/Source/World/Scene.cs | 2 +- 7 files changed, 17 insertions(+), 23 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 870eea2..5f7af80 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -46,10 +46,8 @@ public BvhNode(List srcObjects, int start, int end, AbstractSampler Preprocess() @@ -78,10 +75,8 @@ public List Preprocess() private int BoxCompare(IHittable a, IHittable b, int axis) { - if (!a.BoundingBox(out var boxA) || !b.BoundingBox(out var boxB)) - { - throw new Exception("No bounding box"); - } + var boxA = a.BoundingBox(); + var boxB = b.BoundingBox(); return boxA.Min[axis].CompareTo(boxB.Min[axis]); } diff --git a/RayTracer/Source/Models/Cylinder.cs b/RayTracer/Source/Models/Cylinder.cs index 1a15889..1130f57 100644 --- a/RayTracer/Source/Models/Cylinder.cs +++ b/RayTracer/Source/Models/Cylinder.cs @@ -148,17 +148,16 @@ private void CapHitTest(ref Ray ray, ref HitInfo hit, float from, float to, ref } // https://www.iquilezles.org/www/articles/diskbbox/diskbbox.htm - public override bool BoundingBox(out AABB outputBox) + public override AABB BoundingBox() { var normal = _normal.Normalized(); float x = (float) Math.Sqrt(1 - normal.X * normal.X); float y = (float) Math.Sqrt(1 - normal.Y * normal.Y); float z = (float) Math.Sqrt(1 - normal.Z * normal.Z); Vector3 e = new Vector3(x, y, z) * Scale; - outputBox = new AABB( + return new AABB( Vector3.ComponentMin(_top - e, _bottom - e), Vector3.ComponentMax(_top + e, _bottom + e)); - return true; } private void GetCylinderUV(Vector3 hitPoint, ref Vector2 uv) diff --git a/RayTracer/Source/Models/Model.cs b/RayTracer/Source/Models/Model.cs index 1f05c04..05073ce 100644 --- a/RayTracer/Source/Models/Model.cs +++ b/RayTracer/Source/Models/Model.cs @@ -54,7 +54,7 @@ public Model Unload() public abstract bool HitTest(Ray ray, ref HitInfo hit, float from, float to); - public virtual bool BoundingBox(out AABB outputBox) + public virtual AABB BoundingBox() { throw new System.NotImplementedException(); } diff --git a/RayTracer/Source/Models/Sphere.cs b/RayTracer/Source/Models/Sphere.cs index 778bfd4..b99bd3d 100644 --- a/RayTracer/Source/Models/Sphere.cs +++ b/RayTracer/Source/Models/Sphere.cs @@ -63,10 +63,9 @@ public override Mesh GetMesh() return Mesh; } - public override bool BoundingBox(out AABB outputBox) + public override AABB BoundingBox() { - outputBox = new AABB(Position - new Vector3(Scale), Position + new Vector3(Scale)); - return true; + return new AABB(Position - new Vector3(Scale), Position + new Vector3(Scale)); } private (List, List) GetVertexList(short rings, short sectors) diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index 5c486d9..5328a99 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -27,9 +27,9 @@ private protected override void LoadInternal() throw new Exception("Triangle is used only in ray tracing"); } - public override bool BoundingBox(out AABB outputBox) + public override AABB BoundingBox() { - outputBox = new AABB( + var outputBox = new AABB( Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]),_vertices[2]), Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]),_vertices[2]) ); @@ -48,7 +48,8 @@ public override bool BoundingBox(out AABB outputBox) outputBox.Max = vec; } } - return true; + + return outputBox; } //source: https://www.scratchapixel.com/code.php?id=9&origin=/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle diff --git a/RayTracer/Source/RayTracing/IHittable.cs b/RayTracer/Source/RayTracing/IHittable.cs index 7bab75a..ae1c889 100644 --- a/RayTracer/Source/RayTracing/IHittable.cs +++ b/RayTracer/Source/RayTracing/IHittable.cs @@ -7,6 +7,6 @@ public interface IHittable { bool HitTest(Ray ray, ref HitInfo hit, float from, float to); List Preprocess(); - bool BoundingBox(out AABB outputBox); + AABB BoundingBox(); } } \ No newline at end of file diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index b6b89fe..7b2416f 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -54,7 +54,7 @@ public bool HitTest(Ray ray, ref HitInfo hit, float from, float to) return hitAnything; } - public bool BoundingBox(out AABB outputBox) + public AABB BoundingBox() { throw new System.NotImplementedException(); } From edb9d7af7093a4a0287e30b5aab728a7a878b832 Mon Sep 17 00:00:00 2001 From: p-flis Date: Tue, 5 Jan 2021 00:54:48 +0100 Subject: [PATCH 14/16] moved sampler init to bvhnode constructor --- RayTracer/Source/BVH/BvhNode.cs | 6 ++++-- RayTracer/Source/World/Scene.cs | 10 +--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index 5f7af80..e0c198f 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -8,15 +8,17 @@ namespace RayTracing.BVH { public class BvhNode : IHittable { + private const int SAMPLES = 10000; private readonly IHittable _left; private readonly IHittable _right; private readonly AABB _box; // https://raytracing.github.io/books/RayTracingTheNextWeek.html#boundingvolumehierarchies - public BvhNode(List srcObjects, int start, int end, AbstractSampler sampler) + public BvhNode(List srcObjects, int start, int end, AbstractSampler sampler = null) { + sampler ??= new ThreadSafeSampler(count => IntSampling.Random(count, 0, 3), SAMPLES); + var objects = srcObjects; - var random = new Random(); int axis = sampler.GetSample(); int Comparator(IHittable a, IHittable b) => BoxCompare(a, b, axis); diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 7b2416f..9b98f75 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -11,20 +11,12 @@ namespace RayTracing.World { public class Scene : IHittable { - private const int SAMPLES = 10000; public bool BvhMode { get; set; } = true; public List Models { get; } = new List(); public List Hittables { get; } = new List(); public List Lights { get; } = new List(); public AmbientLight AmbientLight { get; set; } - private readonly AbstractSampler _axisSampler; - - public Scene() - { - _axisSampler = new ThreadSafeSampler(count => IntSampling.Random(count, 0, 3), SAMPLES); - } - public void AddModel(Model model) { Models.Add(model); @@ -86,7 +78,7 @@ private List BvhPreprocess() } } - var node = new BvhNode(hittablesToBvh, 0, hittablesToBvh.Count, _axisSampler); + var node = new BvhNode(hittablesToBvh, 0, hittablesToBvh.Count); Hittables.AddRange(planes); Hittables.Add(node); return Hittables; From 47c3c84d274efba09bd9172ab188342c1ef188b8 Mon Sep 17 00:00:00 2001 From: p-flis Date: Tue, 5 Jan 2021 00:59:39 +0100 Subject: [PATCH 15/16] ctrl alt L --- RayTracer/Source/Models/CustomModel.cs | 2 +- RayTracer/Source/Models/Model.cs | 6 +++--- RayTracer/Source/Models/Rectangle.cs | 2 +- RayTracer/Source/Models/Triangle.cs | 12 ++++++------ RayTracer/Source/World/Scene.cs | 5 +++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/RayTracer/Source/Models/CustomModel.cs b/RayTracer/Source/Models/CustomModel.cs index f0ed739..fdb97b4 100644 --- a/RayTracer/Source/Models/CustomModel.cs +++ b/RayTracer/Source/Models/CustomModel.cs @@ -38,7 +38,7 @@ public override Mesh GetMesh() { return Mesh; } - + public override List Preprocess() { return MeshToTriangles(); diff --git a/RayTracer/Source/Models/Model.cs b/RayTracer/Source/Models/Model.cs index 05073ce..877ec44 100644 --- a/RayTracer/Source/Models/Model.cs +++ b/RayTracer/Source/Models/Model.cs @@ -53,7 +53,7 @@ public Model Unload() } public abstract bool HitTest(Ray ray, ref HitInfo hit, float from, float to); - + public virtual AABB BoundingBox() { throw new System.NotImplementedException(); @@ -70,8 +70,8 @@ public List MeshToTriangles() if (Mesh == null) return hittables; Matrix4 modelMatrix = GetModelMatrix(); - - for (var index = 0; index < Mesh.Indices.Count; index+=3) + + for (var index = 0; index < Mesh.Indices.Count; index += 3) { Vector3 v1 = Mesh.GetPostion(index); Vector3 v2 = Mesh.GetPostion(index + 1); diff --git a/RayTracer/Source/Models/Rectangle.cs b/RayTracer/Source/Models/Rectangle.cs index 27a6b74..a271433 100644 --- a/RayTracer/Source/Models/Rectangle.cs +++ b/RayTracer/Source/Models/Rectangle.cs @@ -101,7 +101,7 @@ public override Mesh GetMesh() { return Mesh; } - + public override List Preprocess() { return MeshToTriangles(); diff --git a/RayTracer/Source/Models/Triangle.cs b/RayTracer/Source/Models/Triangle.cs index 5328a99..87a7f3a 100644 --- a/RayTracer/Source/Models/Triangle.cs +++ b/RayTracer/Source/Models/Triangle.cs @@ -26,23 +26,23 @@ private protected override void LoadInternal() { throw new Exception("Triangle is used only in ray tracing"); } - + public override AABB BoundingBox() { var outputBox = new AABB( - Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]),_vertices[2]), - Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]),_vertices[2]) - ); + Vector3.ComponentMin(Vector3.ComponentMin(_vertices[0], _vertices[1]), _vertices[2]), + Vector3.ComponentMax(Vector3.ComponentMax(_vertices[0], _vertices[1]), _vertices[2]) + ); const float thickness = (float) 1e-5; const float threshold = (float) 1e-7; for (int i = 0; i < 3; i++) { - if(Math.Abs(outputBox.Max[i] - outputBox.Min[i]) < threshold) + if (Math.Abs(outputBox.Max[i] - outputBox.Min[i]) < threshold) { var vec = outputBox.Min; vec[i] -= thickness; outputBox.Min = vec; - + vec = outputBox.Max; vec[i] += thickness; outputBox.Max = vec; diff --git a/RayTracer/Source/World/Scene.cs b/RayTracer/Source/World/Scene.cs index 9b98f75..d448c4c 100644 --- a/RayTracer/Source/World/Scene.cs +++ b/RayTracer/Source/World/Scene.cs @@ -58,9 +58,10 @@ private List StandardPreprocess() { Hittables.AddRange(((IHittable) model).Preprocess()); } + return Hittables; } - + private List BvhPreprocess() { Hittables.Clear(); @@ -83,7 +84,7 @@ private List BvhPreprocess() Hittables.Add(node); return Hittables; } - + public List Preprocess() { if (BvhMode) From c12bcf30d72fac4e5df5bc553cc47d9b36f853f9 Mon Sep 17 00:00:00 2001 From: p-flis Date: Tue, 5 Jan 2021 01:23:26 +0100 Subject: [PATCH 16/16] ifs to switch --- RayTracer/Source/BVH/BvhNode.cs | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/RayTracer/Source/BVH/BvhNode.cs b/RayTracer/Source/BVH/BvhNode.cs index e0c198f..2531fc5 100644 --- a/RayTracer/Source/BVH/BvhNode.cs +++ b/RayTracer/Source/BVH/BvhNode.cs @@ -23,29 +23,25 @@ public BvhNode(List srcObjects, int start, int end, AbstractSampler BoxCompare(a, b, axis); int objectSpan = end - start; - if (objectSpan == 1) + switch (objectSpan) { - _left = _right = objects[start]; - } - else if (objectSpan == 2) - { - if (Comparator(objects[start], objects[start + 1]) < 0) - { + case 1: + _left = _right = objects[start]; + break; + case 2 when Comparator(objects[start], objects[start + 1]) < 0: _left = objects[start]; _right = objects[start + 1]; - } - else - { + break; + case 2: _left = objects[start + 1]; _right = objects[start]; - } - } - else - { - objects.Sort(start, objectSpan - 1, new FuncComparer(Comparator)); - int mid = start + objectSpan / 2; - _left = new BvhNode(objects, start, mid, sampler); - _right = new BvhNode(objects, mid, end, sampler); + break; + default: + objects.Sort(start, objectSpan - 1, new FuncComparer(Comparator)); + int mid = start + objectSpan / 2; + _left = new BvhNode(objects, start, mid, sampler); + _right = new BvhNode(objects, mid, end, sampler); + break; } var boxLeft = _left.BoundingBox();