FunTracer: Defining the ray and viewport

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)


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) |> 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