From 1fe0847a1ee8afbca41466337a9da71c3c4cbf15 Mon Sep 17 00:00:00 2001 From: Patrick Stevens Date: Sun, 11 Apr 2021 14:45:04 +0100 Subject: [PATCH] Moveable camera --- RayTracing.App/SampleImages.fs | 12 ++++++------ RayTracing.Test/TestPlane.fs | 11 +++++++---- RayTracing/Camera.fs | 21 +++++++++----------- RayTracing/Plane.fs | 35 +++++++++++++++++++++++++++++++--- RayTracing/Point.fs | 3 +++ 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/RayTracing.App/SampleImages.fs b/RayTracing.App/SampleImages.fs index 0141606..45c634d 100644 --- a/RayTracing.App/SampleImages.fs +++ b/RayTracing.App/SampleImages.fs @@ -48,7 +48,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make 0.0 0.0 0.0 let camera = - Camera.makeBasic 2.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) + Camera.makeBasic 2.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 400 { Objects = @@ -64,7 +64,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make 0.0 0.0 0.0 let camera = - Camera.makeBasic 2.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) + Camera.makeBasic 2.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 400 { Objects = @@ -82,7 +82,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make 0.0 0.0 0.0 let camera = - Camera.makeBasic 7.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) + Camera.makeBasic 7.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 200 { Objects = @@ -115,7 +115,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make 0.0 0.0 0.0 let camera = - Camera.makeBasic 7.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) + Camera.makeBasic 7.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 1200 { Objects = @@ -141,7 +141,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make 0.0 0.0 0.0 let camera = - Camera.makeBasic 1.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) + Camera.makeBasic 1.0 aspectRatio origin (Vector.make 0.0 0.0 1.0 |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 300 { Objects = @@ -168,7 +168,7 @@ module SampleImages = let aspectRatio = 16.0 / 9.0 let origin = Point.make -2.0 2.0 -1.0 let camera = - Camera.makeBasic 1.0 aspectRatio origin (Point.differenceToThenFrom (Point.make 0.0 0.0 1.0) origin |> Vector.unitise |> Option.get) + Camera.makeBasic 1.0 aspectRatio origin (Point.differenceToThenFrom (Point.make 0.0 0.0 1.0) origin |> Vector.unitise |> Option.get) (Vector.make 0.0 1.0 0.0) let pixels = 200 { Objects = diff --git a/RayTracing.Test/TestPlane.fs b/RayTracing.Test/TestPlane.fs index e43e535..6ad84d5 100644 --- a/RayTracing.Test/TestPlane.fs +++ b/RayTracing.Test/TestPlane.fs @@ -11,10 +11,13 @@ module TestPlane = let ``Orthogonalise does make orthogonal vectors`` () = let property (p : Plane) : bool = let orth = Plane.orthonormalise p |> Option.get - let v1, v2 = Plane.basis orth - Float.equal (UnitVector.dot (Ray.vector v1) (Ray.vector v2)) 0.0 - && Float.equal (UnitVector.dot (Ray.vector v1) (Ray.vector v1)) 1.0 - && Float.equal (UnitVector.dot (Ray.vector v2) (Ray.vector v2)) 1.0 + let v1, v2 = Plane.basis (Vector.make 0.0 1.0 0.0) orth + let dotVectors = UnitVector.dot (Ray.vector v1) (Ray.vector v2) + let v1Length = UnitVector.dot (Ray.vector v1) (Ray.vector v1) + let v2Length = UnitVector.dot (Ray.vector v2) (Ray.vector v2) + Float.equal dotVectors 0.0 + && Float.equal v1Length 1.0 + && Float.equal v2Length 1.0 property |> Prop.forAll (Arb.fromGen TestUtils.planeGen) diff --git a/RayTracing/Camera.fs b/RayTracing/Camera.fs index 30ecb5c..d737f03 100644 --- a/RayTracing/Camera.fs +++ b/RayTracing/Camera.fs @@ -27,32 +27,29 @@ type Camera = [] module Camera = + + /// View angle is in radians (specified arbitrarily) let makeBasic (focalLength : float) (aspectRatio : float) (origin : Point) (viewDirection : UnitVector) + (viewUp : Vector) : Camera = let height = 2.0 - let basis = UnitVector.basis 3 - let xAxis = - basis.[0] - |> Ray.make origin - let yAxis = - basis.[1] - |> Ray.make origin - let view = Ray.make origin viewDirection + let corner = Ray.walkAlong view focalLength + + let viewPlane = Plane.makeNormalTo' corner viewDirection + let xAxis, yAxis = Plane.basis viewUp viewPlane { FocalLength = focalLength ViewportHeight = height ViewportWidth = aspectRatio * height View = view - ViewportXAxis = - Ray.parallelTo (Ray.walkAlong view focalLength) xAxis - ViewportYAxis = - Ray.parallelTo (Ray.walkAlong view focalLength) yAxis + ViewportXAxis = xAxis + ViewportYAxis = yAxis SamplesPerPixel = 50 } \ No newline at end of file diff --git a/RayTracing/Plane.fs b/RayTracing/Plane.fs index 22987a0..444b096 100644 --- a/RayTracing/Plane.fs +++ b/RayTracing/Plane.fs @@ -19,6 +19,29 @@ type OrthonormalPlane = [] module Plane = + let makeNormalTo (point : Point) (Vector (x, y, z) as v) : OrthonormalPlane = + let v1 = + if Float.equal z 0.0 then + Vector.make 0.0 0.0 1.0 + else + Vector.make 1.0 1.0 ((-x - y) / z) + let v2 = + Vector.cross v v1 + |> Vector.unitise + |> Option.get + let v1 = + v1 + |> Vector.unitise + |> Option.get + + { + Point = point + V1 = v1 + V2 = v2 + } + + let inline makeNormalTo' (point : Point) (UnitVector v) = makeNormalTo point v + let orthonormalise (plane : Plane) : OrthonormalPlane option = let coeff = UnitVector.dot plane.V1 plane.V2 let vec2 = @@ -41,6 +64,12 @@ module Plane = Point = Ray.origin r1 } - let basis (plane : OrthonormalPlane) : Ray * Ray = - Ray.make plane.Point plane.V1, - Ray.make plane.Point plane.V2 + /// Construct a basis for this plane, whose second ("up") component is `viewUp` when projected onto the plane. + let basis (viewUp : Vector) (plane : OrthonormalPlane) : Ray * Ray = + let viewUp = Vector.unitise viewUp |> Option.get + let v1Component = UnitVector.dot plane.V1 viewUp + let v2Component = UnitVector.dot plane.V2 viewUp + let v2 = Vector.sum (UnitVector.scale v1Component plane.V1) (UnitVector.scale v2Component plane.V2) |> Vector.unitise |> Option.get + let v1 = Vector.sum (UnitVector.scale v2Component plane.V1) (UnitVector.scale (-v1Component) plane.V2) |> Vector.unitise |> Option.get + Ray.make plane.Point v1, + Ray.make plane.Point v2 diff --git a/RayTracing/Point.fs b/RayTracing/Point.fs index 54e5955..8227109 100644 --- a/RayTracing/Point.fs +++ b/RayTracing/Point.fs @@ -47,6 +47,9 @@ module Vector = let make (x : float) (y : float) (z : float) = Vector (x, y, z) + let cross (Vector (x, y, z)) (Vector (a, b, c)) : Vector = + make (y * c - z * b) (z * a - x * c) (x * b - a * y) + [] module UnitVector = let rec random (floatProducer : FloatProducer) (dimension : int) : UnitVector =