In order to trace a ray we have to decide if a given ray hits a object in our scene. In this post I want to show you how to do this for the classic case of a sphere.

Indeed – for now – a object in a scene will be nothing other than a way to get a intersection of a ray with this object, together with some additional data helping us to shade the point. To be precise if a ray hits a object we will need:

- the point where the ray hits the object
- the distance from the start of the ray
- the normal on the surface of the object at this point (a vector perpendicular to the surface at the hit-point) and
- the color of the object at this point.

All this directly translates to the following types:

type HitResult = { Ray : Ray; Distance : float; Pos : Point; Normal : Direction; Color : Color } type SceneObj = { HitTest : Ray -> HitResult option }

In the remainder of this post I will talk about how to get all this from a sphere.

## Defining a sphere in 3D

So what is a sphere – well Wikipedia explains it really nice and we will use just this:

“A sphere centered at a point in 3D and having radius is the set of all points in 3D having distance from ”:

Of course this is equivalent to

and for this we already know a nice interpretation using the dot-product:

.

Next let’s look at the ray. A ray starting at point and pointing in the direction is the set of all point: . (note that should be non-negative as we are only interested in points in “front” of the start).

Of course we now combine those two equations into:

with unknown .

Setting , and using as is a unit-vector we get:

a simple quadratic equation in .

As we want to trace a ray and get the first hit-point we are only interested in the minimum of the positive ’s.

The normal of a point on the sphere is just the direction of to the point (just the vector from to that point normalized) so this all translates into:

let Create (m : Vector3, radius : float, color : Color) : SceneObj = let r2 = radius * radius let hitTest (ray : Ray) = let getPt t = ray.Start + t*ray.Direction let getResult t = let pos = getPt t let normal = Vector.Normalize (pos - m) { Ray = ray; Distance = t; Pos = pos; Normal = normal; Color = color } let ts = let v0 = m - ray.Start let b = -2.0*(v0<*>ray.Direction) let c = (v0 <*> v0) - r2 solveQuadEquation (1.0, b, c) match ts |> List.filter IsPositive with | [t] -> t |> getResult |> Some | [a; b] -> (min a b) |> getResult |> Some | _ -> None { HitTest = hitTest }

using the helper function

let private solveQuadEquation (a : float, b : float, c : float) = let rad = b*b - 4.0*a*c let k0 = b / (-2.0*a) match rad with | Negative -> [] | NearZero -> [ k0 ] | Positive -> let sqrtRad = (sqrt rad)/(2.0 * a) [ k0 + sqrtRad; k0 - sqrtRad ]

to solve quadratic equations together with the helpers we defined in here to handle floats-near zero:

[< AutoOpen >] module FloatHelpers = let ZeroThreshold = 0.0000000001 let IsNearZero (f : float) = abs f <= ZeroThreshold let IsPositive (f : float) = f > ZeroThreshold let IsNegative (f : float) = f < -ZeroThreshold let (|NotNearZero|_|) (f : float) = if IsNearZero f then None else Some f let (|Negative|NearZero|Positive|) (f : float) = if f |> IsNegative then Negative elif f |> IsPositive then Positive else NearZero

## Performance remarks:

I did not spent to much time on performance as I think at this point clarity rules. There are a lot improvements to be found here – for example in the solving of the quadratic equation as we would only need the form with . As this isn’t going to get a real-time ray tracer anyhow I don’t think that this really matter at all but there might be some tweaking at the end of the series.

That’s it for now – next time we will put all of this together to render a simple sphere – finally

Pingback: FunTracer: Defining the ray and viewport | getting #er()