added materials

master
Rostyslav Hnatyshyn 2 years ago
parent 9a6ba0adce
commit 099e77e9a3
  1. 33
      app/Main.hs
  2. 12
      src/Hittable.hs
  3. 12
      src/Material.hs
  4. 4
      src/Maths.hs

@ -9,6 +9,7 @@ import Hittable
import Linear.V2 import Linear.V2
import Linear.V3 import Linear.V3
import Linear.Vector import Linear.Vector
import Material
import Maths import Maths
import Ray import Ray
import Text.Printf import Text.Printf
@ -22,12 +23,19 @@ type ImageDimensions = (Int, Int)
type ViewPortDimensions = (Double, Double) type ViewPortDimensions = (Double, Double)
-- TODO -- TODO
-- DONE add multiple samples per pixel -- antialiasing
-- fix coordinate system
-- DONE fix random number generator
backgroundColor = V3 0.0 0.0 0.0 backgroundColor = V3 0.0 0.0 0.0
scatter :: (RandomGen g) => Ray Double -> Hit -> Rand g (Ray Double)
scatter (Ray o d) (Hit t p n (Lambertian c)) = do
r <- randV3
let target = p ^+^ colorWithNormal (unitVector n) ^+^ r
return $ Ray p (target ^-^ p)
scatter (Ray o d) (Hit t p n (Metal c)) = do
let reflected = reflect (unitVector d) n
return $ Ray p reflected
getRayColor :: (Shape s, RandomGen g) => [s] -> Int -> Ray Double -> Rand g (V3 Double) getRayColor :: (Shape s, RandomGen g) => [s] -> Int -> Ray Double -> Rand g (V3 Double)
getRayColor ws d r = do getRayColor ws d r = do
let h = getBestHit (mapMaybe (testHit r) ws) let h = getBestHit (mapMaybe (testHit r) ws)
@ -37,19 +45,17 @@ getRayColor ws d r = do
getRayColor' :: (Shape s, RandomGen g) => Ray Double -> Maybe Hit -> [s] -> Int -> Rand g (V3 Double) getRayColor' :: (Shape s, RandomGen g) => Ray Double -> Maybe Hit -> [s] -> Int -> Rand g (V3 Double)
getRayColor' (Ray o d) Nothing _ _ = do return $ colorGradient (unitVector d) getRayColor' (Ray o d) Nothing _ _ = do return $ colorGradient (unitVector d)
getRayColor' (Ray o d) _ _ 0 = do return $ colorGradient (unitVector d) getRayColor' (Ray o d) _ _ 0 = do return $ colorGradient (unitVector d)
getRayColor' (Ray o d) (Just (Hit t p n)) ws depth = getRayColor' (Ray o d) (Just (Hit t p n m)) ws depth = do
do let a = attenuation m
r <- randV3 ray <- scatter (Ray o d) (Hit t p n m)
let target = p ^+^ colorWithNormal (unitVector n) ^+^ r nextVal <- getRayColor ws (depth - 1) ray
nextVal <- getRayColor ws (depth - 1) (Ray p (target ^-^ p)) return (a * nextVal)
return (0.5 *^ nextVal)
colorGradient :: V3 Double -> V3 Double colorGradient :: V3 Double -> V3 Double
colorGradient (V3 x y z) = do colorGradient (V3 x y z) = do
let t = 0.5 * (y + 1.0) let t = 0.5 * (y + 1.0)
(1.0 - t) *^ V3 1.0 1.0 1.0 + t *^ V3 0.5 0.7 1.0 (1.0 - t) *^ V3 1.0 1.0 1.0 + t *^ V3 0.5 0.7 1.0
-- testHit needs to take into account other hits, not here
getBestHit [] = Nothing getBestHit [] = Nothing
getBestHit (h : hs) = Just (getBestHit' hs h) getBestHit (h : hs) = Just (getBestHit' hs h)
@ -98,9 +104,10 @@ main :: IO ()
main = do main = do
let w = 400 let w = 400
let h = 400 let h = 400
-- weirdly, the bottom circle appears on top and bottom let world =
-- Circle (V3 0.0 (-10.05) 1.0) 5] [ Circle (V3 0.0 0.0 (-1.0)) 0.25 (Metal (V3 0.8 0.8 0.8)),
let world = [Circle (V3 0.0 0.0 (-1.0)) 0.25, Circle (V3 0.0 (-10.25) (-1.0)) 10] Circle (V3 0.0 (-10.25) (-1.0)) 10 (Lambertian (V3 0.7 0.3 0.3))
]
let coords = reverse $ range ((-1 * (w `div` 2), -1 * (h `div` 2)), ((w `div` 2) - 1, (h `div` 2) - 1)) -- "canvas" let coords = reverse $ range ((-1 * (w `div` 2), -1 * (h `div` 2)), ((w `div` 2) - 1, (h `div` 2) - 1)) -- "canvas"
colors <- mapM (\x -> rayTrace x (w, h) (1.0, 1.0) world) coords colors <- mapM (\x -> rayTrace x (w, h) (1.0, 1.0) world) coords
let convertedColors = map v3ToColor colors let convertedColors = map v3ToColor colors

@ -2,16 +2,16 @@ module Hittable (Shape (..), Circle (..), Hit (..)) where
import Linear.V3 import Linear.V3
import Linear.Vector import Linear.Vector
import Material
import Maths import Maths
import Ray import Ray
-- section 6.4 will be the key here
class Shape s where class Shape s where
testHit :: Ray Double -> s -> Maybe Hit testHit :: Ray Double -> s -> Maybe Hit
data Circle = Circle (V3 Double) Double data Circle = Circle (V3 Double) Double Material
data Hit = Hit {root :: Double, p :: V3 Double, n :: V3 Double} data Hit = Hit {root :: Double, p :: V3 Double, n :: V3 Double, m :: Material}
getFaceNormal :: Ray Double -> V3 Double -> V3 Double getFaceNormal :: Ray Double -> V3 Double -> V3 Double
getFaceNormal (Ray o d) n getFaceNormal (Ray o d) n
@ -20,15 +20,15 @@ getFaceNormal (Ray o d) n
-- t_min 0 t_max infinity... need closest so far -- t_min 0 t_max infinity... need closest so far
instance Shape Circle where instance Shape Circle where
testHit (Ray o d) (Circle center r) = testHit (Ray o d) (Circle center r m) =
if discriminant < 0.0 if discriminant < 0.0
then Nothing then Nothing
else else
if root > 0.001 if root > 0.001
then Just (Hit root p (getFaceNormal (Ray o d) ((p - center) ^/ r))) then Just (Hit root p (getFaceNormal (Ray o d) ((p - center) ^/ r)) m)
else else
if rootP > 0.001 if rootP > 0.001
then Just (Hit rootP pp (getFaceNormal (Ray o d) ((pp - center) ^/ r))) then Just (Hit rootP pp (getFaceNormal (Ray o d) ((pp - center) ^/ r)) m)
else Nothing else Nothing
where where
co = o - center co = o - center

@ -0,0 +1,12 @@
module Material (Material (..), attenuation) where
import Linear.V3
import Linear.Vector
import Maths
import Ray
data Material = Lambertian (V3 Double) | Metal (V3 Double)
-- attenuation :: (Material m) => m -> V3 Double
attenuation (Lambertian c) = c
attenuation (Metal c) = c

@ -4,6 +4,7 @@ module Maths
unitVector, unitVector,
randV3, randV3,
rand, rand,
reflect,
) )
where where
@ -44,3 +45,6 @@ vectorLength v = sqrt (lengthSquared v)
unitVector :: (Floating f) => V3 f -> V3 f unitVector :: (Floating f) => V3 f -> V3 f
unitVector v = v ^/ vectorLength v unitVector v = v ^/ vectorLength v
reflect :: (Floating f) => V3 f -> V3 f -> V3 f
reflect v n = v - (2 * (v `dotP` n)) *^ n

Loading…
Cancel
Save