69c54cc530fa953e144771c03eccc5a9

This is my first serious haskell project. It's kinda-working, but currently only flat shading. I'd like to get it down in size so I can work on more interesting parts of it.

Using GHC 6.8.2 on ubuntu, output is a 800x600 BMP file (for right now)

Raytracer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import System.IO
import Data.Word
import Foreign
import Data.Char

main :: IO ()
main = do
  handle <- openBinaryFile "test.bmp" WriteMode
  putBin handle header
  print $ head rays
  putBin handle bitmap
  hClose handle


type Vector = (Double, Double, Double)

type Color = (Word8, Word8, Word8)

type Ray = (Vector, Vector)

type Sphere = (Vector, Double)

type Light = Sphere

type Plane = Sphere

type Primitive = (Sphere, Color)

type Scene = (Primitive, [Primitive], [Primitive])

parMap = map

-- arg can't deal with this in anything other than characters. trying to write bytes hurts my head
header :: [Word8]
header = parMap (fromIntegral . ord) $ fileHeader ++ infoHeader
           where 
             fileHeader = "BM\54\249\54\0\0\0\0\0\54\0\0\0"
             infoHeader = "\40\0\0\0\32\3\0\0\88\2\0\0\1\0\24\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

putBin x y = (mapM new y) >>= mapM ((flip (hPutBuf x)) 1)

bitmap :: [Word8]
bitmap = concat $ parMap unTuple3 colors

unTuple3 :: Color -> [Word8]
unTuple3 (a,b,c) = [a,b,c]

camera :: Vector
camera = (0.0, 0.0, -5.0)

createRay :: Double -> Double -> Ray
createRay y x = (camera, (normalizeVector ((x,y,0) `subtractVector` camera)))

rayx :: [Double]
rayx = [8.0,7.98..(-7.98)]

rayy :: [Double]
rayy = [6.0,5.98..(-5.98)]


createRays :: [Double] -> [Ray]
createRays [] = []
createRays xs = (parMap (createRay (head xs)) rayx) ++ (createRays (tail xs))

rays :: [Ray]
rays = createRays rayy

-- reverse because BMP is a fucked up format
colors = parMap (findIntersection scene) $ reverse rays

scene :: Scene
scene = createScene plane spheres lights

plane :: Primitive
plane = (((0, (-0.39), 0), 4.4), (128,128,128))

spheres :: [Primitive]
spheres = bigSphere : littleSphere : []

bigSphere :: Primitive
bigSphere = (((1.0, (-0.8), 3.0), 0.5), (255,128,128))
littleSphere :: Primitive
littleSphere = ((((-5.5), (-0.5), 7.0), 0.4), (128,255,128))

lights = light1 : light2 : []
light1 = (((0.0, 5.0, 5.0), 0.02), (128, 128, 255))
light2 = (((2.0, 5.0, 1.0), 0.02), (255, 128, 255))

createScene :: Primitive -> [Primitive] -> [Primitive] -> Scene
createScene x y z = (x, y, z)

colorFromRay r (distance, ((center, radius), color)) = color

findIntersection :: Scene -> Ray -> Color
findIntersection (plane, spheres, lights) r = colorFromRay r $ foldl (pickShortestDistance) (1000, ((camera, 0.1), (255,255,255))) $ [intersectPlane plane r] ++ (intersectSpheres (spheres ++ lights) r )

pickShortestDistance :: (Double, Primitive) -> (Double, Primitive) -> (Double, Primitive)
pickShortestDistance (x, y) (a, b) = if x < a || (a < 0 && x > 0) then (x, y) else (a, b)               

intersectSpheres :: [Primitive] -> Ray -> [(Double, Primitive)]
intersectSpheres [] _ = []
intersectSpheres spheres r = (intersectSphere (head spheres) r) : (intersectSpheres (tail spheres) r) 

intersectSphere :: Primitive -> Ray -> (Double, Primitive)
intersectSphere ((center, radius), color) (origin, direction) = let destination = normalizeVector $ center `subtractVector` origin
                                                                    b = - ( destination `dotProduct` (normalizeVector direction))
                                                                    c = (destination `dotProduct` destination) - (radius * radius)
                                                                    d = b * b - c
                                                          
                                                                in
                                                                  if d > 0 then
                                                                      ((-b) - (sqrt d), ((center, radius),color))
                                                                  else
                                                                      (10000, ((center, radius),color))

intersectLight ((center, radius), color) (origin, direction) =  let destination = normalizeVector $ center `subtractVector` origin
                                                                    b = - ( destination `dotProduct` (normalizeVector direction))
                                                                    c = (destination `dotProduct` destination) - (radius * radius)
                                                                    d = b * b - c
                                                          
                                                                in
                                                                  if d > 0 then
                                                                      ((-b) - (sqrt d), ((center, radius),(255,255,255)))
                                                                  else
                                                                      (10000, ((center, radius),color))

intersectPlane :: ((Vector, Double), Color) -> Ray -> (Double, Primitive)
intersectPlane ((normal, distance), color) (origin, direction) = let d = normal `dotProduct` (direction `subtractVector` origin)
                                                                     n = normal `dotProduct` (normal `subtractVector` origin)
                                                                 in
                                                                   if d > 0 then
                                                                      (n / d, ((normal, distance),color))
                                                                   else
                                                                      (10000, ((normal, distance),color))

normalizeVector :: Vector -> Vector
normalizeVector v = scaleVector (1 / lengthVector v) v

scaleVector :: Double -> Vector -> Vector
scaleVector a (x,y,z) = (x*a, y*a, z*a)

lengthVector :: Vector -> Double
lengthVector (x,y,z) = sqrt ( x^2 + y^2 + z^2)

addVector :: Vector -> Vector -> Vector
addVector (a,b,c) (x,y,z) = (a+x, b+y, z+c)

subtractVector :: Vector -> Vector -> Vector
subtractVector (a,b,c) (x,y,z) = (a-x, b-y, c-z)

dotProduct :: Vector -> Vector -> Double
dotProduct (a,b,c) (x,y,z) = a*x + b*y + c*z

crossProduct :: Vector -> Vector -> Vector
crossProduct (a,b,c) (x,y,z) = ((b * z - c*y), (c*x - a*z), (a*y - b*x))

Refactorings

No refactoring yet !

Your refactoring





Format Copy from initial code

or Cancel