Today we start adding another primitive to the scene – a plane. This is what we are looking for:
and here is the code that will finally render this:
module SampleScene =
let redMaterial = { Color = Colors.Red; Specular = 1.0; Reflection = 0.7 }
let greenMaterial = { Color = Colors.Green; Specular = 0.5; Reflection = 0.1 }
let blueMaterial = { Color = Colors.Blue; Specular = 1.0; Reflection = 0.9 }
let private scene =
let eye = Vector.Create(0.0, 0.0, -10.0)
let center = Vector.Zero
let up = Vector.Create(0.0, 1.0, 0.0)
let right = Vector.Create(1.0, 0.0, 0.0)
let vp = new ViewPort (eye, center, up, right, 20.0, 20.0)
let s = new Scene(vp)
Sphere.Create (Vector.Create(-5.0, -8.0, 15.0), 4.0, redMaterial)
|> s.AddObject
Sphere.Create (Vector.Create(5.0, -8.0, 15.0), 4.0, redMaterial)
|> s.AddObject
Sphere.Create (Vector.Create(0.0, -8.0, 7.0), 4.0, redMaterial)
|> s.AddObject
Sphere.Create (Vector.Create(0.0, 9.0, 16.0), 7.0, blueMaterial)
|> s.AddObject
Plane.Create (Vector.Create(0.0, -14.0, 0.0), Vector.Create(0.0, 1.0, 0.0), greenMaterial)
|> s.AddObject
let sun = PointLight.CreateWhite(Vector.Create(1.0, 20.0, -10.0))
s.AddLight(sun)
let dir = DirectionalLight.CreateWhite(Vector.Create (0.0, -1.0, 0.1))
s.AddLight(dir)
s
But before going into the implementation of the Plane.Create method let’s first discuss the simple math behind this.
Representation of a plane
We are going to use a particular easy and useful representation for this – again using the dot-product and it’s property to indicate perpendicular vectors by being zero when used with those.
Suppose you’ve got a point you know lying in the plane and you’ve got a normal vector to the plane (there are two lying on the same line and in our case you have to choose well for lighting purposes but for this bit of math it’s indifferent because we will only shoot at zero
).
Now for another point in the plane we must have
and so we can define the plane to be all points having this property (if you have one such point
with
then
is perpendicular to the normal and as
lies in the plane
must do so too.
Remark depending on how you like to define your plane (for example one point on it and two linear independent directions on the plane) you can proof all this very easy using the properties of the dot-product –see Wikipedia on this for a very good intro.
Intersecting a plane with a line
So how can we know if a line hits a plane and where does it hit?
That’s easy now. Remember we represented a line by a point on it and it’s direction
so the line is
. Now we only have to plug this into the representation of the plane and solve for
:
so if
and if there is no solution so no hit point.
Rays instead of lines
The only difference for a ray is that we are not interested in negative ’s – indeed we want it to be positive to have some distance from the start-point.
Implementation in F#
this is just a literal translation of the math we discussed so far:
module Plane =
let Create (a : Point, n : Direction, material : Material) : SceneObj =
let getPointOnRay (ray : Ray) (t : float) =
if t |> IsPositive then
let pos = ray.Start + t*ray.Direction
{ Ray = ray; Distance = t;
Pos = pos; Normal = n;
Material = material }
|> Some
else
None
let intersectRayWithPlane (ray : Ray) : float option =
let k = ray.Direction <*> n
match k with
| NearZero -> None
| _ -> let l = (a - ray.Start) <*> n
Some (l/k)
let hitTest ray = ray |> intersectRayWithPlane |> Option.bind (getPointOnRay ray)
{ HitTest = hitTest }
Note that we took the normal to define the plane as the normal on that plane – so watch it’s direction if you want lighting and reflection on the plane to work our usual way.
I don’t know if you’re aware of it, but this set of articles is a great resource! Not only it is good for F# beginners, it is also a very good reference for F# ray tracing :)
Keep on the great job!
thank you very much :D – will try