commit
c02b17a3ff
@ -0,0 +1,104 @@ |
|||||||
|
module Main where |
||||||
|
|
||||||
|
import Control.Monad (mapM) |
||||||
|
import Control.Monad.Random |
||||||
|
import Data.Ix |
||||||
|
import Data.Maybe |
||||||
|
import Debug.Trace |
||||||
|
import Hittable |
||||||
|
import Linear.V2 |
||||||
|
import Linear.V3 |
||||||
|
import Linear.Vector |
||||||
|
import Maths |
||||||
|
import Ray |
||||||
|
import Text.Printf |
||||||
|
|
||||||
|
type Color = (Double, Double, Double) |
||||||
|
|
||||||
|
type Coord = (Int, Int) |
||||||
|
|
||||||
|
type ImageDimensions = (Int, Int) |
||||||
|
|
||||||
|
type ViewPortDimensions = (Double, Double) |
||||||
|
|
||||||
|
-- TODO |
||||||
|
-- add multiple samples per pixel |
||||||
|
-- fix coordinate system |
||||||
|
-- DONE fix random number generator |
||||||
|
|
||||||
|
backgroundColor = V3 0.0 0.0 0.0 |
||||||
|
|
||||||
|
-- getRayColor :: (Shape s, RandomGen g) => [s] -> Int -> Ray Double -> Rand g (V3 Double) |
||||||
|
-- need to accumulate somewhere |
||||||
|
getRayColor ws d r = do |
||||||
|
let h = getBestHit (mapMaybe (testHit r) ws) |
||||||
|
v <- getRayColor' r h ws d |
||||||
|
return $ v |
||||||
|
|
||||||
|
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' _ _ _ 0 = do return $ backgroundColor |
||||||
|
getRayColor' (Ray o d) (Just (Hit t p n)) ws depth = |
||||||
|
do |
||||||
|
r <- randV3 |
||||||
|
let target = p ^+^ n ^+^ r |
||||||
|
nextVal <- (getRayColor ws (depth - 1) (Ray p (target ^-^ p))) |
||||||
|
return $ colorWithNormal (calculateRayNormal (Ray o d) t) ^+^ (0.5 *^ nextVal) |
||||||
|
|
||||||
|
colorGradient :: V3 Double -> V3 Double |
||||||
|
colorGradient (V3 x y z) = do |
||||||
|
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 |
||||||
|
|
||||||
|
getBestHit :: [Hit] -> Maybe Hit |
||||||
|
getBestHit [] = Nothing |
||||||
|
getBestHit (h : hs) = Just (getBestHit' hs h) |
||||||
|
|
||||||
|
getBestHit' :: [Hit] -> Hit -> Hit |
||||||
|
getBestHit' (h : hs) bestHit = |
||||||
|
if root h > root bestHit |
||||||
|
then getBestHit' hs h |
||||||
|
else getBestHit' hs bestHit |
||||||
|
getBestHit' [] hit = hit |
||||||
|
|
||||||
|
colorWithNormal :: V3 Double -> V3 Double |
||||||
|
colorWithNormal v = 0.5 *^ (v ^+^ V3 1.0 1.0 1.0) |
||||||
|
|
||||||
|
rayTrace :: (Shape s, RandomGen g) => (Int, Int) -> ImageDimensions -> ViewPortDimensions -> [s] -> Rand g (V3 Double) |
||||||
|
rayTrace (x, y) (w, h) (vw, vh) world = do |
||||||
|
getRayColor world 50 (buildRay (xf * (vw / wf)) (yf * (vh / hf))) |
||||||
|
where |
||||||
|
xf = fromIntegral x |
||||||
|
yf = fromIntegral y |
||||||
|
wf = fromIntegral (w - 1) |
||||||
|
hf = fromIntegral (h - 1) |
||||||
|
|
||||||
|
colorToString :: Color -> IO () |
||||||
|
colorToString (x, y, z) = tupToString (xr, yr, zr) |
||||||
|
where |
||||||
|
xr = round (x * 255.0) |
||||||
|
yr = round (y * 255.0) |
||||||
|
zr = round (z * 255.0) |
||||||
|
|
||||||
|
tupToString :: (Show a) => (a, a, a) -> IO () |
||||||
|
tupToString (x, y, z) = putStrLn (show x ++ " " ++ show y ++ " " ++ show z) |
||||||
|
|
||||||
|
v3ToColor :: V3 Double -> Color |
||||||
|
v3ToColor (V3 x y z) = (x, y, z) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
let w = 400 |
||||||
|
let h = 400 |
||||||
|
-- weirdly, the bottom circle appears on top and bottom |
||||||
|
let world = [Circle (V3 0.0 0.0 1.0) 0.25, Circle (V3 0.0 (-10.05) 1.0) 10] |
||||||
|
let coords = range ((-1 * (w `div` 2), -1 * (h `div` 2)), ((w `div` 2) - 1, (h `div` 2) - 1)) -- "canvas" |
||||||
|
--let aspect = 16.0 / 9.0 |
||||||
|
g <- getStdGen |
||||||
|
let colors = map (\x -> foldl (^+^) (V3 0.0 0.0 0.0) $ replicate 50 $ evalRand (rayTrace x (w - 1, h - 1) (1.0, 1.0) world) g) coords |
||||||
|
let convertedColors = map v3ToColor colors |
||||||
|
--mapM_ print colors |
||||||
|
putStrLn "P3" |
||||||
|
putStrLn (show w ++ " " ++ show h) |
||||||
|
putStrLn "255" |
||||||
|
mapM_ colorToString convertedColors |
@ -0,0 +1,51 @@ |
|||||||
|
name: wasm-raytracer |
||||||
|
version: 0.1.0.0 |
||||||
|
github: "githubuser/wasm-raytracer" |
||||||
|
license: BSD3 |
||||||
|
author: "Author name here" |
||||||
|
maintainer: "example@example.com" |
||||||
|
copyright: "2022 Author name here" |
||||||
|
|
||||||
|
extra-source-files: |
||||||
|
- README.md |
||||||
|
- ChangeLog.md |
||||||
|
|
||||||
|
# Metadata used when publishing your package |
||||||
|
# synopsis: Short description of your package |
||||||
|
# category: Web |
||||||
|
|
||||||
|
# To avoid duplicated efforts in documentation and dealing with the |
||||||
|
# complications of embedding Haddock markup inside cabal files, it is |
||||||
|
# common to point users to the README.md file. |
||||||
|
description: Please see the README on GitHub at <https://github.com/githubuser/wasm-raytracer#readme> |
||||||
|
|
||||||
|
dependencies: |
||||||
|
- base >= 4.7 && < 5 |
||||||
|
- linear |
||||||
|
- random |
||||||
|
- mtl |
||||||
|
- MonadRandom |
||||||
|
library: |
||||||
|
source-dirs: src |
||||||
|
|
||||||
|
executables: |
||||||
|
wasm-raytracer-exe: |
||||||
|
main: Main.hs |
||||||
|
source-dirs: app |
||||||
|
ghc-options: |
||||||
|
- -threaded |
||||||
|
- -rtsopts |
||||||
|
- -with-rtsopts=-N |
||||||
|
dependencies: |
||||||
|
- wasm-raytracer |
||||||
|
|
||||||
|
tests: |
||||||
|
wasm-raytracer-test: |
||||||
|
main: Spec.hs |
||||||
|
source-dirs: test |
||||||
|
ghc-options: |
||||||
|
- -threaded |
||||||
|
- -rtsopts |
||||||
|
- -with-rtsopts=-N |
||||||
|
dependencies: |
||||||
|
- wasm-raytracer |
@ -0,0 +1,29 @@ |
|||||||
|
module Hittable (Shape (..), Circle (..), Hit (..)) where |
||||||
|
|
||||||
|
import Linear.V3 |
||||||
|
import Linear.Vector |
||||||
|
import Maths |
||||||
|
import Ray |
||||||
|
|
||||||
|
class Shape s where |
||||||
|
testHit :: Ray Double -> s -> Maybe Hit |
||||||
|
|
||||||
|
data Circle = Circle (V3 Double) Double |
||||||
|
|
||||||
|
data Hit = Hit {root :: Double, p :: V3 Double, n :: V3 Double} |
||||||
|
|
||||||
|
instance Shape Circle where |
||||||
|
testHit (Ray o d) (Circle center r) = |
||||||
|
if discriminant < 0.0 |
||||||
|
then Nothing |
||||||
|
else Just (Hit root p (p - center ^/ r)) |
||||||
|
where |
||||||
|
co = o - center |
||||||
|
a = lengthSquared d -- a vector dotted with itself is = lengthSquared |
||||||
|
b = d `dotP` co |
||||||
|
c = lengthSquared co - r ^ 2 |
||||||
|
discriminant = b ^ 2 - a * c |
||||||
|
root = (b * (-1.0) - sqrt discriminant) / a |
||||||
|
p = rayAt (Ray o d) root |
||||||
|
|
||||||
|
-- would make sense to build a Functor for shapes |
@ -0,0 +1,44 @@ |
|||||||
|
module Maths |
||||||
|
( dotP, |
||||||
|
lengthSquared, |
||||||
|
unitVector, |
||||||
|
randV3, |
||||||
|
rand, |
||||||
|
) |
||||||
|
where |
||||||
|
|
||||||
|
import Control.Monad.Random |
||||||
|
import Control.Monad.State (State, evalState, get, put) |
||||||
|
import Debug.Trace |
||||||
|
import Linear.V3 |
||||||
|
import Linear.Vector |
||||||
|
import System.Random |
||||||
|
|
||||||
|
rand :: (RandomGen g) => Rand g Double |
||||||
|
rand = getRandomR (-1.0, 1.0 :: Double) |
||||||
|
|
||||||
|
randV3 :: (RandomGen g) => Rand g (V3 Double) |
||||||
|
randV3 = do |
||||||
|
x <- rand |
||||||
|
y <- rand |
||||||
|
z <- rand |
||||||
|
return $ V3 x y z |
||||||
|
|
||||||
|
-- _ -> randV3 |
||||||
|
|
||||||
|
--inf = read "Infinity" |
||||||
|
|
||||||
|
degToRadians :: (Floating a) => a -> a |
||||||
|
degToRadians d = (d * pi) / 180.0 |
||||||
|
|
||||||
|
dotP :: (Num a) => V3 a -> V3 a -> a |
||||||
|
dotP (V3 a b c) (V3 d e f) = a * d + b * e + c * f |
||||||
|
|
||||||
|
lengthSquared :: (Num a) => V3 a -> a |
||||||
|
lengthSquared (V3 x y z) = x ^ 2 + y ^ 2 + z ^ 2 |
||||||
|
|
||||||
|
vectorLength :: (Floating f) => V3 f -> f |
||||||
|
vectorLength v = sqrt (lengthSquared v) |
||||||
|
|
||||||
|
unitVector :: (Floating f) => V3 f -> V3 f |
||||||
|
unitVector v = v ^/ vectorLength v |
@ -0,0 +1,24 @@ |
|||||||
|
module Ray |
||||||
|
( Ray (..), |
||||||
|
rayAt, |
||||||
|
calculateRayNormal, |
||||||
|
buildRay, |
||||||
|
) |
||||||
|
where |
||||||
|
|
||||||
|
import Linear.V3 |
||||||
|
import Linear.Vector |
||||||
|
import Maths |
||||||
|
|
||||||
|
data Ray f = Ray (V3 f) (V3 f) |
||||||
|
|
||||||
|
-- P(t) = A + tB; A is origin, B is direction |
||||||
|
rayAt :: (Floating f) => Ray f -> f -> V3 f |
||||||
|
rayAt (Ray origin dir) t = origin + (t *^ dir) |
||||||
|
|
||||||
|
-- get the normal of the point at t |
||||||
|
calculateRayNormal :: (Floating f) => Ray f -> f -> V3 f |
||||||
|
calculateRayNormal r t = unitVector (rayAt r t ^-^ V3 0.0 0.0 1.0) |
||||||
|
|
||||||
|
buildRay :: (Floating f) => f -> f -> Ray f |
||||||
|
buildRay u v = Ray (V3 0.0 0.0 0.0) (V3 v u 1.0) |
@ -0,0 +1,67 @@ |
|||||||
|
# This file was automatically generated by 'stack init' |
||||||
|
# |
||||||
|
# Some commonly used options have been documented as comments in this file. |
||||||
|
# For advanced use and comprehensive documentation of the format, please see: |
||||||
|
# https://docs.haskellstack.org/en/stable/yaml_configuration/ |
||||||
|
|
||||||
|
# Resolver to choose a 'specific' stackage snapshot or a compiler version. |
||||||
|
# A snapshot resolver dictates the compiler version and the set of packages |
||||||
|
# to be used for project dependencies. For example: |
||||||
|
# |
||||||
|
# resolver: lts-3.5 |
||||||
|
# resolver: nightly-2015-09-21 |
||||||
|
# resolver: ghc-7.10.2 |
||||||
|
# |
||||||
|
# The location of a snapshot can be provided as a file or url. Stack assumes |
||||||
|
# a snapshot provided as a file might change, whereas a url resource does not. |
||||||
|
# |
||||||
|
# resolver: ./custom-snapshot.yaml |
||||||
|
# resolver: https://example.com/snapshots/2018-01-01.yaml |
||||||
|
resolver: |
||||||
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/15.yaml |
||||||
|
|
||||||
|
# User packages to be built. |
||||||
|
# Various formats can be used as shown in the example below. |
||||||
|
# |
||||||
|
# packages: |
||||||
|
# - some-directory |
||||||
|
# - https://example.com/foo/bar/baz-0.0.2.tar.gz |
||||||
|
# subdirs: |
||||||
|
# - auto-update |
||||||
|
# - wai |
||||||
|
packages: |
||||||
|
- . |
||||||
|
# Dependency packages to be pulled from upstream that are not in the resolver. |
||||||
|
# These entries can reference officially published versions as well as |
||||||
|
# forks / in-progress versions pinned to a git hash. For example: |
||||||
|
# |
||||||
|
# extra-deps: |
||||||
|
# - acme-missiles-0.3 |
||||||
|
# - git: https://github.com/commercialhaskell/stack.git |
||||||
|
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a |
||||||
|
# |
||||||
|
# extra-deps: [] |
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps |
||||||
|
# flags: {} |
||||||
|
|
||||||
|
# Extra package databases containing global packages |
||||||
|
# extra-package-dbs: [] |
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path |
||||||
|
# system-ghc: true |
||||||
|
# |
||||||
|
# Require a specific version of stack, using version ranges |
||||||
|
# require-stack-version: -any # Default |
||||||
|
# require-stack-version: ">=2.7" |
||||||
|
# |
||||||
|
# Override the architecture used by stack, especially useful on Windows |
||||||
|
# arch: i386 |
||||||
|
# arch: x86_64 |
||||||
|
# |
||||||
|
# Extra directories used by stack for building |
||||||
|
# extra-include-dirs: [/path/to/dir] |
||||||
|
# extra-lib-dirs: [/path/to/dir] |
||||||
|
# |
||||||
|
# Allow a newer minor version of GHC than the snapshot specifies |
||||||
|
# compiler-check: newer-minor |
@ -0,0 +1,74 @@ |
|||||||
|
cabal-version: 1.12 |
||||||
|
|
||||||
|
-- This file has been generated from package.yaml by hpack version 0.34.7. |
||||||
|
-- |
||||||
|
-- see: https://github.com/sol/hpack |
||||||
|
|
||||||
|
name: wasm-raytracer |
||||||
|
version: 0.1.0.0 |
||||||
|
description: Please see the README on GitHub at <https://github.com/githubuser/wasm-raytracer#readme> |
||||||
|
homepage: https://github.com/githubuser/wasm-raytracer#readme |
||||||
|
bug-reports: https://github.com/githubuser/wasm-raytracer/issues |
||||||
|
author: Author name here |
||||||
|
maintainer: example@example.com |
||||||
|
copyright: 2022 Author name here |
||||||
|
license: BSD3 |
||||||
|
license-file: LICENSE |
||||||
|
build-type: Simple |
||||||
|
extra-source-files: |
||||||
|
README.md |
||||||
|
ChangeLog.md |
||||||
|
|
||||||
|
source-repository head |
||||||
|
type: git |
||||||
|
location: https://github.com/githubuser/wasm-raytracer |
||||||
|
|
||||||
|
library |
||||||
|
exposed-modules: |
||||||
|
Hittable |
||||||
|
Maths |
||||||
|
Ray |
||||||
|
other-modules: |
||||||
|
Paths_wasm_raytracer |
||||||
|
hs-source-dirs: |
||||||
|
src |
||||||
|
build-depends: |
||||||
|
MonadRandom |
||||||
|
, base >=4.7 && <5 |
||||||
|
, linear |
||||||
|
, mtl |
||||||
|
, random |
||||||
|
default-language: Haskell2010 |
||||||
|
|
||||||
|
executable wasm-raytracer-exe |
||||||
|
main-is: Main.hs |
||||||
|
other-modules: |
||||||
|
Paths_wasm_raytracer |
||||||
|
hs-source-dirs: |
||||||
|
app |
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N |
||||||
|
build-depends: |
||||||
|
MonadRandom |
||||||
|
, base >=4.7 && <5 |
||||||
|
, linear |
||||||
|
, mtl |
||||||
|
, random |
||||||
|
, wasm-raytracer |
||||||
|
default-language: Haskell2010 |
||||||
|
|
||||||
|
test-suite wasm-raytracer-test |
||||||
|
type: exitcode-stdio-1.0 |
||||||
|
main-is: Spec.hs |
||||||
|
other-modules: |
||||||
|
Paths_wasm_raytracer |
||||||
|
hs-source-dirs: |
||||||
|
test |
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N |
||||||
|
build-depends: |
||||||
|
MonadRandom |
||||||
|
, base >=4.7 && <5 |
||||||
|
, linear |
||||||
|
, mtl |
||||||
|
, random |
||||||
|
, wasm-raytracer |
||||||
|
default-language: Haskell2010 |
Loading…
Reference in new issue