This time we will have a look at two types that will help us on your way. First I will show you how I implemented a ray-type for representing the most important aspect of ray-tracing itself and then I will show you the viewport-type I use to generate all starting-rays for the image we want to generate.
The ray-type
My thinking behind a “ray” is very simple. It’s just a line starting at some point together with a direction. The start-point will be just that – a point in 3D space and the direction should be a direction-vector in 3D space having unit-length (length 1). So the type will imply this constraint (direction have length 1). For this I choose to give two static members for construction of the ray. One for point+direction and a one for constructing a ray from a start-point and another point lying on the ray – the second will make certain that the unit-length constraint is met, the first one will NOT check (for performance-reasons):
type Ray = struct
val Start : Point
val Direction : Direction
new (s, d) = { Start = s; Direction = d }
static member Create (start, direction) =
new Ray (start, direction)
static member FromPoints (start, pointOnRay) =
let direction = Vector.Normalize (pointOnRay - start)
Ray.Create (start, direction)
end
The view-port type
This one will be responsible for creating the a raster of points on the viewport and ultimately a ray for each of these raster points pointing from the eye-point in the world-space to the raster-point on the viewport – have a look at this diagram for details:
type RayRaster = Ray[,]
type ViewPort(eye : Point, center : Point, dirUp : Direction, dirRight : Direction, height : float, width : float) =
let createRaster (resX : int, resY : int) : Point[,] =
let dX = width / (float resX)
let dY = height / (float resY)
let start = center - ((width-dX)/2.0)*dirRight - ((height-dY)/2.0)*dirUp
Array2D.init resX resY (fun x y -> start + (float x*dX)*dirRight + (float y*dY)*dirUp)
let createRay (viewPortPoint : Point) =
Ray.FromPoints (eye, viewPortPoint)
member vp.CreateRaster (resX, resY) : RayRaster =
createRaster(resX, resY) |> Array2D.map createRay
I think the code is straight forward – resX/resY stands for Resolution in X and Y direction (on the viewport – so in the right/up-direction the type get’s in it’s constructor). In the diagram up points just up (vertical) and right just right (horizontal).
That’s it for now – next time we will talk about hitting spheres …
Pingback: FunTracer: Intro and let there be light | getting #er