DEV Community

James Green
James Green

Posted on

Raytracing – Part I: Overview

πŸ“ TL;DR - Haskell lends itself nicely to basic raytracing, simple code (not mine) creates incredible first results.


Table of Contents

Β 1. Intro
Β 2. Vectors
Β 3. Geometric functions
Β 4. Conclusion


Intro

Hello! This is my first post, and realistically, my first real attempt at blogging. Without further ado, we'll jump straight into the content.

We’re looking at functional programming. Specifically, we’re going to look at implementing some basic raytracing in Haskell. Firstly, the code for this project is all on my GitHub. Hopefully, at some point moving forward I’ll have a website set-up to show off the results.

There are a couple of basic resources I’m using to have a first stab at the project:

  • Htrace is the initial work we’ll be analysing and expanding upon.
  • This incredible video explaining how modern CGI and ray tracing works. The implementation of any of this will be post the initial analysis.

Vectors

Let's jump into some code.

type Vector3 = (Float, Float, Float)
Enter fullscreen mode Exit fullscreen mode

Here we have a definition of a vector, as you can see the syntax here is pretty self-explanatory. A 3-dimensional vector is an ordered 3-tuple. Next let's talk about some operations you'll be familiar with.

add :: Vector3 -> Vector3 -> Vector3
add (x,y,z) (a,b,c) = (a+x, b+y, c+z)

sub :: Vector3 -> Vector3 -> Vector3
sub (a,b,c) (x,y,z) = (a-x, b-y, c-z)

squared_mag :: Vector3 -> Float
squared_mag (x,y,z) = (x*x + y*y + z*z)

mag :: Vector3 -> Float
mag v = sqrt (squared_mag v)
Enter fullscreen mode Exit fullscreen mode

All of the above are pretty basic mathematically, and similarly the code is faily trivial. We could alternativly, and worsely (though pointfreely), define:

squared_mag :: Vector3 -> Float
squared_mag = sum map (**2)
Enter fullscreen mode Exit fullscreen mode

But then we'd have to worry about defining map and sum for Vector3 and suddenly I've lost interest.

scalarmult :: Vector3 -> Float -> Vector3
scalarmult (x,y,z) c = (x*c, y*c, z*c)

dot :: Vector3 -> Vector3 -> Float
dot (x,y,z) (a,b,c) = x*a + b*y + c*z

cross :: Vector3 -> Vector3 -> Vector3
cross (a,b,c) (x,y,z) = (b*z + c*y, -(a*z + c*x), a*y + b*x)

normalize :: Vector3 -> Vector3
normalize v
  | (mag v) /= 0 = scalarmult v (1 / mag v) 
  | otherwise    = (0,0,0)

neg :: Vector3 -> Vector3
neg (x,y,z) = (-x,-y,-z)
Enter fullscreen mode Exit fullscreen mode

These again are all pretty standard, the only change I'd make is to make scalarmult left multiplication:

scalarmult :: Float -> Vector3 -> Vector3
scalarmult c (x,y,z) = (c*x, c*y, c*z)

neg :: Vector3 -> Vector3
neg = scalarmult (-1)
Enter fullscreen mode Exit fullscreen mode

Geometric functions

Now we can move into some more geometric mathematics, we define some more datatypes.

type Point3 = Vector3
type Direction3 = Vector3
type Time = Float
type Ray = (Point3, Direction3) -- base and direction

position_at_time :: Ray -> Time -> Point3
position_at_time (base, dir) t = base `add` (scalarmult dir t)
Enter fullscreen mode Exit fullscreen mode

Note the position_at_time function assumes no acceleration (allowed as we're working with light). We'll omit code for the next couple of definitions for brevity, we define a quadratic solver and xor. These are the standard definition.


Conclusion

The next steps are to start looking into colours, I have something substantially different intended for this. So we'll cut this post here for the time being.

Top comments (0)