Point of View

Noticed the previous renders all looked very similar? That’s because I did not really have a notion of “view” or camera implemented. There was a hard-coded thing sitting at (0, 0, -4) and firing rays down the z-axis. I just fixed that and came up with a pinhole camera, which is really the least one can expect from a raytracer. This enables really dramatic renderings. Well, as dramatic as it gets if all you have are spheres and planes… :-)

Three spheres in a dramatic setup

data Ray = Ray {
   rayOrigin :: Point,
   rayDir :: Normal,
   rayMin :: Float,
   rayMax :: Float
   } deriving Show

type Camera = (Float, Float) -> Ray

-- | defines the view for projective camera models
data View = View {
   viewPos :: Point, -- ^ the position of the camera in world space
   viewLookAt :: Point, -- ^ the "look-at" point world space
   viewUp :: Normal, -- ^ the "up" vector
   viewFocalLength :: Float, -- ^ focal length
   viewAspect :: Float -- ^ aspect ratio of image plane
   }

-- | computes a point on the image plane
viewPoint :: View -> (Float, Float) -> Point
viewPoint (View pos la up dist aspect) (u, v) = \
      center `add` (scalMul right u') `add` (scalMul up' v') where
   center = pos `add` (scalMul dir dist)
   right =  normalize $ up `cross` dir
   up' = cross right dir
   dir = normalize $ sub la pos
   u' = u * aspect - 0.5
   v' = v - 0.5

-- | a simple "pinhole" camera
pinHoleCamera :: View -> Camera
pinHoleCamera view uv = Ray pv rd 0 infinity where
   rd = normalize $ sub pv $ viewPos view
   pv = viewPoint view uv

So, the code above is all there is about the camera abstraction I currently have. I think it is pretty self-explanatory, besides that magical (Float, Float) going into the Camera function. These are “normalized device coordinates”–numbers in [0, 1] which allow to sample image plane without knowing the size (in pixels) of the image that is being rendered.

Sure, it would be pretty easy to add a camera doing a thin lens approximation at this point. And depth of field is nice and all… But look at the noise in the image above! These are 100 samples per pixel, rendering took almost 150 minutes–and I doubt adding another level of randomness from sampling the lens would do any good here. So before adding that “cool” stuff I’ll have to go for a better sampling scheme.

PS: I’m sorry the red sphere in the image above looks so ultra-boring.


About this entry