Antialiasing! But a parsing bug.
Will Brown
10 years ago
5 | 5 | |
6 | 6 | data Scene = |
7 | 7 | Scene { |
8 | geom :: [Primitive], | |
9 | 8 | background :: ColorTriple, |
10 | 9 | globalAmbient :: ColorTriple, |
10 | subpixels :: Int, | |
11 | geom :: [Primitive], | |
11 | 12 | lights :: [Light], |
12 | 13 | viewer :: Viewer |
13 | 14 | } deriving (Show, Read) |
80 | 81 | } deriving (Show, Read) |
81 | 82 | |
82 | 83 | data Point2D = |
83 | -- | Describes a point in the image using pixel coordinates | |
84 | Point2D Int Int | |
84 | -- | Describes a point in the image using pixel coordinates, allowing for | |
85 | -- | fractional pixels for subpixel sampling. | |
86 | Point2D Double Double | |
85 | 87 | -- | Describes a point in the image using offsets from the center, where each |
86 | 88 | -- field goes from zero to one. |
87 | 89 | | RelPoint2D Double Double |
94 | 96 | colorm r g b = ColorMaterial (r, g, b) |
95 | 97 | |
96 | 98 | toRelPoint :: Size -> Point2D -> Point2D |
97 | toRelPoint size (Point2D ix iy) = | |
99 | toRelPoint size (Point2D x y) = | |
98 | 100 | RelPoint2D ((x - xc) / xc) ((y - yc) / yc) |
99 | 101 | where xc = fromIntegral (fst size) / 2 |
100 | 102 | yc = fromIntegral (snd size) / 2 |
101 | x = fromIntegral ix | |
102 | y = fromIntegral iy | |
103 | 103 | |
104 | 104 | -- |Creates a viewer |
105 | 105 | view :: Vec3 -- ^ The location of the viewer |
137 | 137 | |
138 | 138 | sumColor :: ColorTriple -> ColorTriple -> ColorTriple |
139 | 139 | sumColor (r1, g1, b1) (r2, g2, b2) = normalizeColor (r1 + r2, g1 + g2, b1 + b2) |
140 | sumColor' (r1, g1, b1) (r2, g2, b2) = (r1 + r2, g1 + g2, b1 + b2) | |
140 | 141 | |
142 | meanColor :: [ColorTriple] -> ColorTriple | |
143 | meanColor colors = div $ foldl1 sumColor' colors | |
144 | where l = fromIntegral $ length colors | |
145 | div (r, g, b) = (r / l, g / l, b / l) |
7 | 7 | import ArtRay.Geometry |
8 | 8 | import ArtRay.Primitives |
9 | 9 | |
10 | pixelColor :: Size -> Scene -> Viewer -> Point2D -> Color | |
10 | pixelColor :: Size -> Scene -> Viewer -> Point2D -> ColorTriple | |
11 | 11 | pixelColor size scene viewer (Point2D ix iy) = |
12 | 12 | pixelColor size scene viewer (toRelPoint size (Point2D ix iy)) |
13 | 13 | pixelColor size scene viewer (RelPoint2D hu hv) = |
14 | colorFrom $ | |
15 | colorAtRay scene ray 0 where ray = pointToRay viewer (RelPoint2D hu hv) | |
14 | colorAtRay scene ray 0 where ray = pointToRay viewer (RelPoint2D hu hv) | |
16 | 15 | |
17 | 16 | colorFor :: Scene |
18 | 17 | -> Primitive -- | The shape to determine the color for |
100 | 99 | intersectWithScene scene ray exclude = |
101 | 100 | mapMaybe (firstIntersection ray) (filter (`notElem` exclude) (geom scene)) |
102 | 101 | |
103 | rayTraceImage :: Scene -> Size -> Point -> Color | |
104 | rayTraceImage scene size (x, y) = | |
105 | pixelColor size scene (viewer scene) (Point2D x y) |
5 | 5 | import Graphics.GD |
6 | 6 | import ArtRay.Geometry |
7 | 7 | |
8 | applyToImage :: (Size -> Point -> Color) -> Image -> IO() | |
8 | -- | Raytrace an image without antialiasing | |
9 | rayTraceImage :: Scene -> Size -> Point2D -> ColorTriple | |
10 | rayTraceImage scene size point@(Point2D x y) = | |
11 | pixelColor size scene (viewer scene) point | |
12 | ||
13 | -- | Raytrace an image with antialiasing by subpixel sampling | |
14 | rayTraceImage' :: Scene -> Size -> Point -> ColorTriple | |
15 | rayTraceImage' scene size (x, y) = | |
16 | meanColor $ map (rayTraceImage scene size) | |
17 | [Point2D (fromIntegral x + x') (fromIntegral y + y') | |
18 | | x' <- [(-0.5),inc..0.5], y' <- [(-0.5),inc..0.5]] | |
19 | where inc = (-0.5) + (1 / (fromIntegral $ subpixels scene)) | |
20 | ||
21 | applyToImage :: (Size -> Point -> ColorTriple) -> Image -> IO() | |
9 | 22 | applyToImage imFunc im = |
10 | 23 | do |
11 | 24 | size <- imageSize im |
12 | sequence_ [setPixel (i,j) (imFunc size (i,j)) im | i <- [0..(fst size)], j <- [0..(snd size)]] | |
25 | sequence_ [setPixel (i,j) (colorFrom $ imFunc size (i,j)) im | |
26 | | i <- [0..(fst size)], j <- [0..(snd size)]] | |
13 | 27 | |
14 | 28 | render :: Scene -> Int -> String -> IO() |
15 | 29 | render scene size outfile = |
16 | 30 | do |
17 | 31 | im <- newImage (size, size) |
18 | applyToImage (rayTraceImage scene) im | |
32 | applyToImage (rayTraceImage' scene) im | |
19 | 33 | savePngFile outfile im |
20 |
6 | 6 | Compilation is handled by the build.sh script. After running build.sh, |
7 | 7 | you can execute ArtRay as follows: |
8 | 8 | |
9 | ./artray [input scene] [output image] | |
9 | ./ar [input scene] [output image] | |
10 | 10 | |
11 | 11 | Output is always written in PNG format. Input is in ArtRay format, which is |
12 | 12 | essentially a bunch of nested data constructors. Better docs for this are on |
0 | 0 | if [ ! -d "build" ]; then |
1 | 1 | mkdir build |
2 | 2 | fi |
3 | ghc ArtRay/Main.hs -o artray -odir build/ -hidir build/ -O | |
3 | ghc ArtRay/Main.hs -o ar -odir build/ -hidir build/ -O |
3 | 3 | center = Vec3 14.0 (-4.0) 0.0, |
4 | 4 | radius = 3.0, |
5 | 5 | material = PhongMaterial { |
6 | specular = (1.0,1.0,1.0), | |
7 | diffuse = (0.7,0.0,0.0), | |
8 | ambient = (1.0,0.0,0.0), | |
9 | phongexp = 4 | |
10 | } | |
6 | specular = (1.0, 1.0, 1.0), | |
7 | diffuse = (0.7, 0.0, 0.0), | |
8 | ambient = (1.0, 0.0, 0.0), | |
9 | phongexp = 4 | |
10 | } | |
11 | 11 | }, |
12 | 12 | |
13 | 13 | Sphere { |
14 | 14 | center = Vec3 10.0 0.0 0.0, |
15 | 15 | radius = 1.0, |
16 | 16 | material = PhongMaterial { |
17 | specular = (1.0,1.0,1.0), | |
18 | diffuse = (0.0,0.7,0.0), | |
19 | ambient = (0.0,1.0,0.0), | |
20 | phongexp = 4 | |
21 | } | |
17 | specular = (1.0, 1.0, 1.0), | |
18 | diffuse = (0.0, 0.7, 0.0), | |
19 | ambient = (0.0, 1.0, 0.0), | |
20 | phongexp = 4 | |
21 | } | |
22 | 22 | }, |
23 | ||
23 | ||
24 | 24 | Plane { |
25 | 25 | pnorm = Vec3 (-2) 0 (-1), |
26 | 26 | point = Vec3 13 0 6, |
27 | 27 | material = ReflectiveMaterial { |
28 | 28 | base = PhongMaterial { |
29 | specular = (0.0,0.0,0.0), | |
30 | diffuse = (0.0,0.0,0.7), | |
31 | ambient = (0.0,0.0,1.0), | |
32 | phongexp = 4 | |
29 | specular = (0.0, 0.0, 0.0), | |
30 | diffuse = (0.0, 0.0, 0.7), | |
31 | ambient = (0.0, 0.0, 1.0), | |
32 | phongexp = 4 | |
33 | 33 | }, |
34 | reflectivity = 0.5 | |
35 | } | |
34 | reflectivity = 0.5 | |
35 | } | |
36 | 36 | } |
37 | 37 | ], |
38 | 38 | |
39 | background = (0.0,0.0,0.0), | |
40 | global_ambient = (0.4,0.4,0.4), | |
39 | subpixels = 4, | |
40 | background = (0.0, 0.0, 0.0), | |
41 | globalAmbient = (0.4, 0.4, 0.4), | |
42 | ||
41 | 43 | lights = [ |
42 | 44 | PhongLight { |
43 | speclight = (1.0,1.0,1.0), | |
44 | difflight = (1.0,1.0,1.0), | |
45 | loclight = Vec3 6.0 4.0 0.0 | |
46 | } | |
47 | ], | |
48 | ||
45 | speclight = (1.0, 1.0, 1.0), | |
46 | difflight = (1.0, 1.0, 1.0), | |
47 | loclight = Vec3 6.0 4.0 0.0 | |
48 | }], | |
49 | ||
49 | 50 | viewer = Viewer { |
50 | 51 | location = Vec3 0.0 0.0 0.0, |
51 | 52 | u = Vec3 0.0 0.422 0.0, |
52 | 53 | v = Vec3 0.0 0.0 0.422, |
53 | f = Vec3 1.0 (-0.2) 0.0 | |
54 | } | |
55 | } | |
56 | ||
54 | f = Vec3 1.0 (-0.2) 0.0 | |
55 | } | |
56 | } |