-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.hs
145 lines (116 loc) · 4.74 KB
/
main.hs
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
{-# LANGUAGE NamedFieldPuns #-}
import System.Console.ArgParser (parsedBy, andBy, reqPos, optFlag, withParseResult)
import Control.Concurrent (getNumCapabilities)
import Control.DeepSeq (force)
import Control.Monad (forM_)
import Control.Parallel.Strategies (Eval, rpar, runEval)
import Control.Monad.Par (runPar, spawn, get)
import Data.Map (Map, fromList, (!))
import Data.Tuple (swap)
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
import System.Environment
import Codec.Picture
import Vectors
import Rays
import qualified Scene as S
import Shapes
import Cameras
import Shaders
import Culling
import AABBs (BoundingBox)
import Integrators (radiance)
import Sampling (lineBatches, squareBatches)
data Invocation = Invocation
{ invInput :: String
, invOutput :: String
, invParMode :: String
}
invocationParser = Invocation
`parsedBy` reqPos "input"
`andBy` reqPos "output"
`andBy` optFlag "sequential" "parallel-mode"
buildCollisionModel :: S.Scene -> [(BoundingBox, Collider Material)]
buildCollisionModel s = zip sceneObjBounds sceneColliders
where
sceneObjects = S.objects s >>= S.expand
sceneColliders = map collideSceneObject sceneObjects
collideSceneObject (S.Sphere p r mId) = collideSphere (mat mId) r p
collideSceneObject (S.Triangle p0 p1 p2 n0 n1 n2 mId) =
collideTriangle (mat mId) p0 p1 p2 n0 n1 n2
sceneObjBounds = map boundSceneObject sceneObjects
boundSceneObject (S.Sphere p r mId) = boundSphere r p
boundSceneObject (S.Triangle {S.p0, S.p1, S.p2, S.materialId=mId}) = boundTriangle p0 p1 p2
mat mId = Material (mats ! mId)
mats :: Map String Shader
mats = fromList [(S.id m, shaderFromDescription m) | m <- S.materials s]
shaderFromDescription desc = case desc of
S.BlinnPhongMaterial id ambient diffuse specular shininess ->
blinnPhong ambient diffuse specular shininess
spectrumToPixel :: Spectrum -> PixelRGBF
spectrumToPixel (Vec3 r g b) = PixelRGBF r g b
type SampleCoordinates = (Int, Int)
type Sample = (SampleCoordinates, Spectrum)
render :: Int -> Int -> [[SampleCoordinates]] ->
(Float -> Float -> Ray) -> (Ray -> Spectrum) ->
[Sample]
render w h batches cast li = concat $ map (map sample) batches
where
sample (u, v) = ((u, v), li $ cast (fromIntegral u) (fromIntegral v))
renderEval :: Int -> Int -> [[SampleCoordinates]] ->
(Float -> Float -> Ray) -> (Ray -> Spectrum) ->
[Sample]
renderEval w h batches cast li =
let batch coords = [ ((u, v), li (cast (fromIntegral u) (fromIntegral v)))
| (u, v) <- coords ]
evals :: [Eval [Sample]]
evals = [ rpar (force (batch coords)) | coords <- batches ]
in concat $ runEval $ sequence evals
renderPar :: Int -> Int -> [[SampleCoordinates]] ->
(Float -> Float -> Ray) -> (Ray -> Spectrum) ->
[Sample]
renderPar w h batches cast li =
let batch coords = [ ((u, v), li (cast (fromIntegral u) (fromIntegral v)))
| (u, v) <- coords ]
par = do
ivars <- mapM (spawn . return . batch) batches
pixelBatches <- mapM get ivars
return $ concat pixelBatches
in runPar par
samplesToImage :: Int -> Int -> [Sample] -> Image PixelRGBF
samplesToImage w h samples =
let uvToIndex u v = w * v + u
image = V.create $ do
img <- MV.new (w * h)
forM_ samples $ \((u, v), spectrum) ->
MV.write img (uvToIndex u v) spectrum
return img
getPixel u v = spectrumToPixel $ image V.! (uvToIndex u v)
in generateImage getPixel w h
roundUpPow2 :: Int -> Int
roundUpPow2 x = 2 ^ (ceiling (logBase 2 (fromIntegral x)))
app invocation = do
numThreads <- getNumCapabilities
sceneFile <- readFile (invInput invocation)
let scene = read sceneFile :: S.Scene
collider = cull (S.cullingMode scene) (buildCollisionModel scene)
camera = S.camera scene
caster = computeInitialRay camera
width = floor $ imW camera
height = floor $ imH camera
li :: Ray -> Spectrum
li = radiance (S.integrator scene) (S.lights scene) collider
nBatches = roundUpPow2 $ max
(32 * numThreads)
width * height `div` (16 * 16)
samplePoints = squareBatches width height nBatches
samples =
case (invParMode invocation) of
"sequential" -> render width height samplePoints caster li
"eval" -> renderEval width height samplePoints caster li
"par" -> renderPar width height samplePoints caster li
img = samplesToImage width height samples
putStrLn $ (show numThreads) ++ " threads, " ++ (show nBatches) ++
" batches, parallel " ++ (invParMode invocation)
savePngImage (invOutput invocation) (ImageRGBF img)
main = withParseResult invocationParser app