This is a really tiny diff compared to the last experiment - I’m just going to add a new material type of ‘specular’.
I factored out a bit of common code (
lightDistanceAndRay), and added
diffuseMaterial :: Color -> Double -> [PointLightSource] -> Ray -> Point -> UnitVector -> Light diffuseMaterial !col !factor !lights _ intersectionPosition surfaceNormal = sumLights $ map diffuseLight lights where diffuseLight (PointLightSource !lightPosition !lightColor) | diffuseFactor > 0 = lightColor `colored` col `scaled` diffuseFactor | otherwise = black where (lightDistance, lightRay) = lightDistanceAndRay intersectionPosition lightPosition lightAttenuation = 1.0 / lightDistance diffuseFactor = factor * (surfaceNormal |.| lightRay) * lightAttenuation specularMaterial :: Double -> Double -> [PointLightSource] -> Ray -> Point -> UnitVector -> Light specularMaterial !factor !shininess !lights (Ray _ !rd) intersectionPosition surfaceNormal = sumLights $ map specularLight lights where specularLight (PointLightSource !lightPosition !lightColor) | cosFactor > 0 = lightColor `scaled` factor `scaled` (cosFactor ** shininess) `scaled` lightAttenuation | otherwise = black where (lightDistance, lightRay) = lightDistanceAndRay intersectionPosition lightPosition lightReflect = surfaceNormal |*| ((surfaceNormal |*| 2) |.| lightRay) |-| lightRay cosFactor = lightReflect |.| neg rd lightAttenuation = 1.0 / lightDistance lightDistanceAndRay :: Point -> Point -> (Double, UnitVector) lightDistanceAndRay intersectionPosition lightPosition = (lightDistance, lightRay) where lightVector = intersectionPosition `to` lightPosition lightDistance = magnitude lightVector lightRay = normalize lightVector
Notice that in
diffuseMaterial, we ignore the
Ray parameter (the ray from camera to surface),
specularMaterial, it’s part of the lighting calculation. Hopefully this makes sense -
a matt material looks roughly the same no matter where you view it from, but a shiny surface will
have different brightness depending on the viewing angle. (If you happen to catch a reflection of a
light, it will appear bright at that point, but if not, the surface at that point will be dim).
Rather than have material variants for only diffuse, only specular, and part-diffuse-part-specular surfaces, I’ve added a method that lets me combine different surface material kinds:
addMaterials :: [Material] -> [PointLightSource] -> Ray -> Point -> UnitVector -> Light addMaterials materials lights ray ixp surfaceNormal = sumLights $ map (\m -> m lights ray ixp surfaceNormal) materials
So with all that in place, the final image looks as follows:
Code is in Github, if you want to take a look.
Published: Monday, July 20, 2015
Hackification.io is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com. I may earn a small commission for my endorsement, recommendation, testimonial, and/or link to any products or services from this website.