(RayTracing) Vec3 클래스

 

거의 모든 그래픽스 프로그램은 벡터와 컬러 클래스를 가지고 있습니다. 많은 시스템들이 4D(3D에 homogeneous coordinate를 더한 기하와 RGB에 alpha를 추가한 컬러)입니다. 강의의 목적으로는 3D 좌표계만으로도 충분합니다. 저희는 vec3 클래스로 컬러, 위치, 방향, 좌표 등등 여러 방도로 사용할겁니다. 몇몇 분들은 이 구조가 맘에 안들 수도 있습니다. location에 color를 더한다던가 말이죠. 좋은 지적입니다만, 저희는 틀리지 않다면 “적은 코드”로 진행하기를 중점에 두고 있습니다. 저희는 vec3 의 alias인 point3, color 를 선언합니다. 두 타입이 vec3 의 alias라고 해서 혼동할 걱정은 하지 마세요.

Implementations

Vector.hs

module Vector where

import           Control.Applicative

data Vec3 a =
  Vec3
    { _x :: a
    , _y :: a
    , _z :: a
    }
  deriving (Eq)

type Vec = Vec3 Float

type Point = Vec3 Float

type Color = Vec3

instance Show a => Show (Vec3 a) where
  show = foldMap ((++ " ") . show)

instance Functor Vec3 where
  fmap f (Vec3 a b c) = Vec3 (f a) (f b) (f c)

instance Foldable Vec3 where
  foldMap f (Vec3 a b c) = f a `mappend` f b `mappend` f c

instance Applicative Vec3 where
  pure a = Vec3 a a a
  Vec3 a b c <*> Vec3 d e f = Vec3 (a d) (b e) (c f)

instance Num a => Num (Vec3 a) where
  (+) = liftA2 (+)
  (*) = liftA2 (*)
  abs = fmap abs
  signum = fmap signum
  negate = fmap negate
  fromInteger = pure . fromInteger

instance Fractional a => Fractional (Vec3 a) where
  (/) = liftA2 (/)
  recip = fmap (1 /)
  fromRational = pure . fromRational

vDot :: Num a => Vec3 a -> Vec3 a -> a
vDot v1 v2 = sum $ v1 * v2

vLength :: Floating a => Vec3 a -> a
vLength v = sqrt $ vDot v v

vLengthSquared :: Floating a => Vec3 a -> a
vLengthSquared v = vDot v v

vUnit :: Floating b => Vec3 b -> Vec3 b
vUnit v = fmap (* k) v
  where
    k = recip . vLength $ v

vCross :: Num a => Vec3 a -> Vec3 a -> Vec3 a
vCross v1 v2 = Vec3 x y z
  where
    x = (_y v1 * _z v2) - (_z v1 * _y v2)
    y = negate $ (_x v1 * _z v2) - (_z v1 * _x v2)
    z = (_x v1 * _y v2) - (_y v1 * _x v2)

Main.hs

module Main where

import qualified Data.ByteString.Char8 as C

import           Vector

data Size =
  Size
    { width  :: Int
    , height :: Int
    }

makeSimplePPM :: Size -> String
makeSimplePPM size =
  let coords = (,) <$> reverse [0 .. height size - 1] <*> [0 .. width size - 1]
   in unlines $ "P3" : size' : "255" : map (show . toColor) coords
  where
    size' = unwords . map show $ [width size, height size]
    toColor (y, x) =
      truncate . (* 255.999) <$>
      Vec3
        (toFloat x / ((+ (-1)) . toFloat . width) size)
        (toFloat y / ((+ (-1)) . toFloat . height) size)
        0.25
      where
        toFloat x = fromIntegral x :: Float

main :: IO ()
main = C.writeFile "res.ppm" . C.pack $ makeSimplePPM (Size 256 256)