This time we are going to add specular lightning – this will add “shiny”ness on objects.
The basic idea is simple: we reflect the currently traced ray, at the hit-point (using the normal of the object at this point) and look at the angle between the outgoing ray and the line from the hit-point to a light source (similar to our basic-shading model). If this is small we add the light-color (scaled by a specular-factor of the object) to our result-color.
Again we can use the cosine and it’s easy representation with the dot-product, but this time we are going raise this value to the 10th power – this will shrink the shiny-spot and intensify it around the point where a incoming ray would be reflected directly into the light source.
So the only hard part is to reflect the ray. To understand the math behind look at this picture:
is the direction of our ray and we can have (projection onto
):
– please not the directions. The resulting reflected direction is then
Translated into F#:
// reflected direction
let reflDir = hit.Ray.Direction - 2.0 * (hit.Normal <*> hit.Ray.Direction) * hit.Normal;
As mentioned above we calculate the specular factor by using the dot-product:
let specF = (reflDir <*> lightDir) |> suppresNeg
let specColor = System.Math.Pow(specF, 10.0) * hit.Specular * light.Color
And our shading-algorithm changes to:
let shade (getSigObjs : Ray -> SceneObj seq) (hit : HitResult) (light : Light) : Color =
// get the direction of the light for this point
let lightDir = hit.Pos |> light.GetDirection
// is the light visible?
let rayToLight = Ray.Create(hit.Pos, lightDir)
let lightVisible = findHitObj getSigObjs rayToLight |> Option.isNone
// reflected direction
let reflDir = hit.Ray.Direction - 2.0 * (hit.Normal <*> hit.Ray.Direction) * hit.Normal;
// lighting factors
let ambient = 0.4
let diffuse = 0.6
let diffF, specF =
if lightVisible then
// helper - we are only interessted in positive values
let suppresNeg x = if x <= 0.0 then 0.0 else x
// get the fraction of diffuse Light (cap at 0)
let diffF = (hit.Normal <*> lightDir) |> suppresNeg
// get the specular factor
let specF = (reflDir <*> lightDir) |> suppresNeg
diffF, specF
else
0.0, 0.0
// calculate the total color by ambient + diffuse + specular light
let diffuseColor = diffuse * diffF * hit.Color * light.Color
let specColor = System.Math.Pow(specF, 10.0) * hit.Specular * light.Color
let ambientColor = ambient * hit.Color * light.Color
// return the shaded color
ambientColor + diffuseColor + specColor
Here we changed the HitResult-type to include the Specular-“shiny”ness for a object:
type HitResult
= { Ray : Ray; Distance : float;
Pos : Point; Normal : Direction;
Color : Color; Specular : float }
This turns the scene from our last article into this:
Pingback: Vector Fun: projection of a vector on another | getting #er