packages feed

GPipe-Core-0.2.3.1: bench/LambdaRay/Schwarzschild.hs

{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE NamedFieldPuns      #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
module LambdaRay.Schwarzschild (frag) where

import           Control.Lens     ((^.))
import           Graphics.GPipe   hiding (lookAt, normalize)
import           LambdaRay.Config
import           Prelude          hiding ((<*))


aspectRatio :: Fractional a => V2 Int -> a
aspectRatio viewPort = fromRational $ toRational y / toRational x
  where V2 x y = viewPort


sqrnorm :: (Metric f, Floating a) => f a -> a
sqrnorm vec = n * n
  where n = norm vec

clip :: (IfB a, OrdB a) => a -> a -> a -> a
clip lo hi x = maxB (minB x hi) lo

xorB :: Boolean b => b -> b -> b
xorB a b = a &&* notB b ||* b &&* notB a


diskOuter, diskOuterSqr, diskInner, diskInnerSqr, diskWidth :: Fractional a => a
diskInner = 2.6
diskOuter = 14
diskWidth = fromRational (14 - 2.6)
diskInnerSqr = fromRational (2.6 * 2.6)
diskOuterSqr = fromIntegral (14 * 14 :: Int)


viewMatrix :: Floating a => V3 a -> V3 (V3 a)
viewMatrix cameraPos = V3 leftVec nupVec frontVec
  where
    lookAt = V3 0 0 0
    upVec = V3 0.2 1 0

    frontVec = signorm (lookAt - cameraPos)
    leftVec = signorm (cross upVec frontVec)
    nupVec = cross frontVec leftVec


data Context a = Context
    { ctxPoint       :: V3 a
    , ctxVelocity    :: V3 a
    , ctxStepsize    :: a
    , ctxObjectColor :: V3 a
    , ctxObjectAlpha :: a
    }

-- instance ShaderType (Context FFloat) x where
--     type ShaderBaseType (Context FFloat) = ShaderBaseType FFloat
--     toBase x ~Context{..} = (toBase x ctxI)
--     fromBase x b = undefined


blendcolors :: Num a => V3 a -> a -> V3 a -> a -> V3 a
blendcolors ca aa cb ba = ca + cb ^* (ba * (1 - aa))

blendalpha :: Num a => a -> a -> a
blendalpha aa ba = aa + ba * (1 - aa)


computeSkyColor :: Real' a => SkyMode a -> V3 a -> V3 a
computeSkyColor SkyBlack _ = V3 0 0 0
computeSkyColor (SkyTexture samp) velocity = skycolor
  where
    vphi = atan2' (velocity ^. _x) (velocity ^. _z)
    vtheta = atan2' (velocity ^. _y) (norm (velocity ^. _xz))

    u = mod'' (vphi+4.5)(2*pi) / (2*pi)
    v = (vtheta+pi/2)/pi

    skycolor = samp (V2 u v)


computeDiskColor :: FFloat -> V3 FFloat -> V3 FFloat -> DiskMode FFloat -> (V3 FFloat, FFloat)
computeDiskColor time velocity newpoint = compute
  where
    -- actual collision point by intersection
    lambdaa = -(newpoint ^. _y / (velocity ^. _y))
    colpoint = newpoint + lambdaa *^ velocity
    colpointsqr = sqrnorm colpoint

    phi = atan2' (colpoint ^. _x) (newpoint ^. _z) + time / 30

    compute DiskSolid = (V3 0.5 0.5 0.5, 1)

    compute (DiskTexture samp) = (diskcolor, diskalpha)
      where
        u = ((phi + 2*pi) `mod''` 2*pi) / (2*pi)
        v = (sqrt colpointsqr - diskInner) / diskWidth

        diskcolor = samp (V2 u v)
        diskalpha = clip 0 1 (sqrnorm diskcolor / 3.0)

    compute DiskGrid = (diskcolor, 1)
      where
        diskcolor = ifThenElse' (phi `mod''` 0.52359 <* 0.261799)
            (V3 1 1 1)
            (V3 0 0 1)


computeHorizonColor :: HorizonMode -> V3 FFloat -> FFloat -> V3 FFloat -> FFloat -> (V3 FFloat, FFloat)
computeHorizonColor HorizonBlack _ _ _ _ = (V3 0 0 0, 1)
computeHorizonColor HorizonGrid oldpoint oldpointsqr newpoint newpointsqr = (horizoncolor, 1)
  where
    lambdaa = 1 - (1 - oldpointsqr) / (newpointsqr - oldpointsqr)
    colpoint = lambdaa *^ newpoint + (1 - lambdaa) *^ oldpoint

    phi = atan2' (colpoint ^. _x) (newpoint ^. _z)
    theta = atan2' (colpoint ^. _y) (norm (newpoint ^. _xz))

    horizoncolor = ifThenElse' (((phi `mod''` 1.04719) <* 0.52359) `xorB` ((theta `mod''` 1.04719) <* 0.52359))
        (V3 1 0 0)
        (V3 0 0 0)


computeVelocity :: Floating a => DistortionMethod -> a -> a -> a -> V3 a -> V3 a -> a -> (V3 a, a)
computeVelocity MethodNone stepsize _ _ velocity _ _ = (velocity, stepsize)
computeVelocity MethodLeapFrog stepsize stepfactor h2 velocity newpoint newpointsqr = (newvelocity, newstepsize)
  where
    accel = (-1.5) * h2 *^ newpoint ^/ (newpointsqr ** 2.5)
    newvelocity = velocity + accel ^* stepsize
    -- step = abs $ norm velocity - norm newvelocity
    newstepsize = stepsize * stepfactor


iteration :: Config FFloat -> RuntimeConfig FFloat -> FFloat -> Context FFloat -> Context FFloat
iteration Config{distortionMethod,diskMode,horizonMode} RuntimeConfig{..} (h2 :: FFloat) ctx = nctx
  where
    oldpoint = ctxPoint ctx -- for intersections
    oldpointsqr = sqrnorm oldpoint

    newpoint = ctxPoint ctx + ctxVelocity ctx ^* ctxStepsize ctx
    newpointsqr = sqrnorm newpoint

    (newvelocity, newstepsize) =
      computeVelocity distortionMethod (ctxStepsize ctx) stepfactor h2 (ctxVelocity ctx) newpoint newpointsqr

    -- whether it just crossed the horizontal plane
    maskCrossing = (oldpoint ^. _y >* 0) `xorB` (newpoint ^. _y >* 0)
    -- whether it's close enough
    maskDistance = newpointsqr <* diskOuterSqr &&* newpointsqr >* diskInnerSqr

    diskMask = maskCrossing &&* maskDistance
    (diskcolor, diskalpha) = ifThenElse' diskMask (computeDiskColor time (ctxVelocity ctx) newpoint diskMode) (0, 0)

    -- event horizon
    horizonMask = newpointsqr <* 1 &&* oldpointsqr >* 1
    (horizoncolor, horizonalpha) = ifThenElse' horizonMask (computeHorizonColor horizonMode oldpoint oldpointsqr newpoint newpointsqr) (0, 0)

    c1 = blendcolors (ctxObjectColor ctx) (ctxObjectAlpha ctx) horizoncolor horizonalpha
    a1 = blendalpha (ctxObjectAlpha ctx) horizonalpha
    c2 = blendcolors c1 a1 diskcolor diskalpha
    a2 = blendalpha a1 diskalpha

    nctx = Context
      { ctxPoint = newpoint
      , ctxVelocity = newvelocity
      , ctxStepsize = newstepsize
      , ctxObjectColor = c2
      , ctxObjectAlpha = a2
      }


frag :: Config FFloat -> V2 Int -> RuntimeConfig FFloat -> V2 FFloat -> V3 FFloat
frag cfg@Config{iterations} viewPort rt@RuntimeConfig{..} (V2 x y) = result
  where
    tanFov = 1.5

    pos = cameraPos + V3 0 0 (time / 3 `mod''` 21)
    -- pos = V3 0 2 (-15.0)

    view = signorm $ viewMatrix pos !* V3 (tanFov * (x - 0.5)) (tanFov * aspectRatio viewPort * (y - 0.5)) 1

    h2 = sqrnorm (cross pos view)

    Context{ctxVelocity, ctxObjectColor, ctxObjectAlpha} =
      foldr (const $ iteration cfg rt h2)
        Context { ctxPoint = pos
                , ctxVelocity = view
                , ctxStepsize = stepsize
                , ctxObjectColor = V3 0 0 0
                , ctxObjectAlpha = 0
                } [0..iterations]

    skycolor = computeSkyColor (skyMode cfg) ctxVelocity

    result = blendcolors ctxObjectColor ctxObjectAlpha skycolor 1