LambdaCube 3D

lambdacube3d.com

LambdaCube 3D API 0.5

We gradually improve the documentation. If you have any question or something is confusing and needs to be clarified please ask us via email or irc on #haskell-game (irc.freenode.net).

Reduction phases

Reduction is done in three different phases:

The runtime CPU and runtime GPU phases are interleaved.

Design principles:

  1. Every reduction step is done in the runtime CPU phase by default.
  2. As an optimization, some reductions are done at runtime GPU instead of runtime CPU. Of course, this optimization step is crucial: without this optimization LambdaCube 3D is useless.
    GPU operations are very slow on CPU, but the CPU implementation of GPU primitives ensures that there is no cheating in the types of GPU primitives.
  3. As an optimization, some reductions are done at compile time CPU instead of runtime CPU.
  4. The programmer can assert that a computation is done in a specific phase via the type system.
    In this way, one can refactor LambdaCube 3D source code without fear of performance regressions.

Principle 1. is not yet implemented: some reductions are only possible to do at the runtime GPU phase.

Principle 4. is not implemented yet: phase information is provided in the comments, not in the types. The compiler code generator automagically determines the phases of expressions.

Basic types

Basic values

type description example values
Bool Boolean values True, False
Int signed integers …, -2, -1, 0, 1, 2, …
Word unsigned integers 0, 1, 2, …
Nat natural numbers 0, 1, 2, …
or Zero, Succ Zero, Succ (Succ Zero), …
Float floating point numbers 0, 3.14, …
Vec n t vectors V3 0.1 0 0, V2 True False
Mat i j t matrices M23F (V2 0 0.1) (V2 1 1) (V3 0 0) :: Mat 2 3 Float

The difference between Word and Nat is that they have different representations. Word values are atomic, whilst Nat values have more structure: for example the pattern Succ a matches all non-zero naturals. Usually Nat is used in types, for example, the dimension of a Vec is a Nat instead of an Int.

The vector constructors are V2, V3 and V4. It is only possible to construct vectors with 2, 3 or 4 dimensions.
For example,

    V3 0 1.1 2 :: Vec 3 Float

The matrix constructors are M22F, M23F, M24F, M32F, M33F, M34F, M42F, M43F and M44F. The matrix constructors needs the columns of the matrices.
For example,

    M23F (V2 0 0.1) (V2 1 1) (V3 0 0) :: Mat 2 3 Float

It is only possible to construct matrices with dimensions i x j where i and j are 2, 3 or 4.
It is only possible to construct martices of float elements.

Basic type families (type functions)

VecScalar        :: Nat -> Type -> Type
MatVecScalarElem :: Type -> Type

If the first argument of VecScalar is 1, then the result is the second argument, otherwise VecScalar is the same as Vec.
Examples:

VecScalar 1 Float = Float
VecScalar 2 Float = Vec 2 Float

MatVecScalarElem gives back the type of elements of matrices and vectors; in case of scalars it is the identity function.
Examples:

MatVecScalarElem Float = Float
MatVecScalarElem (Vec 3 Bool) = Bool
MatVecScalarElem (Mat 3 3 Float) = Float

Basic type classes

type class instances description
Integral Int, Word integral numbers
Signed Float, Int signed numbers
Num Float, Int, Word numbers
Floating Float, Float vectors, Float matrices floating point numbers and containers
Component Bool, Int, Word, Float and its vectors provides zero and one (see below)

Primitive operations

Phase information
Runtime CPU: None of the primitive oparations are accessible.
Runtime GPU: All of the primitive oparations are accessible.
Compile time CPU: Some of the primitive oparations are accessible.

Values

zero :: Component a => a Generic zero value, for example zero = V3 0.0 0.0 0.0 :: Vec 3 Float
one :: Component a => a Generic one value, for example one = V3 1.0 1.0 1.0 :: Vec 3 Float
rgb :: Float -> Float -> Float -> Vec 4 Float
rgb r g b = V4 r g b 1.0
black :: Vec 4 Float same as rgb 0.0 0.0 0.0
gray :: Vec 4 Float same as rgb 0.5 0.5 0.5
silver :: Vec 4 Float same as rgb 0.75 0.75 0.75
white :: Vec 4 Float same as rgb 1.0 1.0 1.0
maroon :: Vec 4 Float same as rgb 0.5 0.0 0.0
red :: Vec 4 Float same as rgb 1.0 0.0 0.0
olive :: Vec 4 Float same as rgb 0.5 0.5 0.0
yellow :: Vec 4 Float same as rgb 1.0 1.0 0.0
green :: Vec 4 Float same as rgb 0.0 0.5 0.0
lime :: Vec 4 Float same as rgb 0.0 1.0 0.0
teal :: Vec 4 Float same as rgb 0.0 0.5 0.5
aqua :: Vec 4 Float same as rgb 0.0 1.0 1.0
navy :: Vec 4 Float same as rgb 0.0 0.0 0.5
blue :: Vec 4 Float same as rgb 0.0 0.0 1.0
purple :: Vec 4 Float same as rgb 0.5 0.0 0.5
fuchsia :: Vec 4 Float same as rgb 1.0 0.0 1.0

Transformation Functions

lookat :: Vec 3 Float -> Vec 3 Float -> Vec 3 Float -> Mat 4 4 Float camera transformation with args: camera position, target position, upward direction
perspective :: Float -> Float -> Float -> Float -> Mat 4 4 Float perspective transformation with args: near plane, far plane, field of view of the y axis (in radians), aspect ratio (i.e. screen’s width/height)
scale :: Float -> Vec 4 Float -> Vec 4 Float scales a vector
rotMatrixX :: Float -> Mat 4 4 Float rotation around X axis, takes the angle in radians
rotMatrixY :: Float -> Mat 4 4 Float rotation around Y axis, takes the angle in radians
rotMatrixZ :: Float -> Mat 4 4 Float rotation around Z axis, takes the angle in radians
rotationEuler :: Float -> Float -> Float -> Mat 4 4 Float rotation using Euler angles
translateBefore4 :: Vec 3 Float -> Mat 4 4 Float translation matrix

Arithmetic Functions

The functions operate component-wise.

(+) :: Num (MatVecElem a) => a -> a -> a addition of two scalar, vector or matrix
(-) :: Num (MatVecElem a) => a -> a -> a substraction of two scalar, vector or matrix
(*) :: Num (MatVecElem a) => a -> a -> a multiplication of two scalar, vector or matrix
(/) :: (Num t, a ~ VecScalar d t) => a -> a -> a division of two scalar or vector
mod :: (Num t, a ~ VecScalar d t) => a -> a -> a modulus of two scalar or vector
neg :: Signed (MatVecScalarElem a) => a -> a unary negation of a scalar, vector or matrix
(+!) :: (t ~ MatVecScalarElem a, Num t) => a -> t -> a addition between (scalar, vector or matrix) and a scalar
(-!) :: (t ~ MatVecScalarElem a, Num t) => a -> t -> a substraction between (scalar, vector or matrix) and a scalar
(*!) :: (t ~ MatVecScalarElem a, Num t) => a -> t -> a multiplication between (scalar, vector or matrix) and a scalar
(/!) :: (Num t, a ~ VecScalar d t) => a -> t -> a division between (scalar, vector or matrix) and a scalar
(%!) :: (Num t, a ~ VecScalar d t) => a -> t -> a modulus between (scalar, vector or matrix) and a scalar

Geometric Functions

length :: (a ~ VecScalar d Float) => a -> Float length of a vector
distance :: (a ~ VecScalar d Float) => a -> a -> Float distance of two vectors
dot :: (a ~ VecScalar d Float) => a -> a -> Float dot product
cross :: (a ~ VecScalar 3 Float) => a -> a -> a cross product
normalize :: (a ~ VecScalar d Float) => a -> a normalize a vector to have length equals to 1
faceforward :: (a ~ VecScalar d Float) => a -> a -> a -> a same as faceforward in GLSL e.g. faceForward n i nRef = if dot nRef i < 0 then n else -n
refract :: (a ~ VecScalar d Float) => a -> a -> a -> a same as refract in GLSL
reflect :: (a ~ VecScalar d Float) => a -> a -> a same as reflect in GLSL

Matrix Functions

transpose :: Mat i j a -> Mat j i a transposed matrix
det :: Mat i i a -> Float determinant
inv :: Mat i i a -> Mat i i a inverse matrix
(.*.) :: Mat i j a -> Mat j k a -> Mat i k a matrix * matrix (multiplication in matrix algebra)
(*.) :: Mat i j a -> Vec j a -> Vec i a matrix * column vector (multiplication in matrix algebra)
(.*) :: Vec i a -> Mat i j a -> Vec j a row vector * matrix (multiplication in matrix algebra)
outer :: Vec j a -> Vec i a -> Mat i j a column vector * row vector (multiplication in matrix algebra)

Vector and Scalar Relational Functions (component-wise)

(<) :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b component-wise compare of x < y
(<=) :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b component-wise compare of x <= y
(>) :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b component-wise compare of x > y
(>=) :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b component-wise compare of x >= y
(==) :: (t ~ MatVecScalarElem a) => a -> a -> Bool x == y
(/=) :: (t ~ MatVecScalarElem a) => a -> a -> Bool x /= y
PrimEqualV :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b x == y (component-wise)
PrimNotEqualV :: (Num t, a ~ VecScalar d t, b ~ VecScalar d Bool) => a -> a -> b x /= y (component-wise)

Logic Functions

(&&) :: Bool -> Bool -> Bool logical and
(||) :: Bool -> Bool -> Bool logical or
xor :: Bool -> Bool -> Bool logical exclusive or
not :: (a ~ VecScalar d Bool) => a -> a logical negation (component-wise)
any :: VecScalar d Bool -> Bool logical or between vector components
all :: VecScalar d Bool -> Bool logical and between vector components

Angle and Trigonometry Functions

The functions operate component-wise.

degrees :: (a ~ VecScalar d Float) => a -> a -> a converts radians to degrees
radians :: (a ~ VecScalar d Float) => a -> a -> a converts degrees to radians
acos :: (a ~ VecScalar d Float) => a -> a arc cosine
acosh :: (a ~ VecScalar d Float) => a -> a arc hyperbolic cosine
asin :: (a ~ VecScalar d Float) => a -> a arc sine
asinh :: (a ~ VecScalar d Float) => a -> a arc hyperbolic sine
atan :: (a ~ VecScalar d Float) => a -> a -> a arc tangent
atan2 :: (a ~ VecScalar d Float) => a -> a -> a arc tangent
atanh :: (a ~ VecScalar d Float) => a -> a -> a arc hyperbolic tangent
cos :: (a ~ VecScalar d Float) => a -> a -> a cosine
cosh :: (a ~ VecScalar d Float) => a -> a -> a hyperbolic cosine
sin :: (a ~ VecScalar d Float) => a -> a -> a sine
sinh :: (a ~ VecScalar d Float) => a -> a -> a hyperbolic sine
tan :: (a ~ VecScalar d Float) => a -> a -> a tangent
tanh :: (a ~ VecScalar d Float) => a -> a -> a hyperbolic tangent

Exponential Functions

The functions operate component-wise.

exp :: (a ~ VecScalar d Float) => a -> a -> a natural exponentiation
log :: (a ~ VecScalar d Float) => a -> a -> a natural logarithm
exp2 :: (a ~ VecScalar d Float) => a -> a -> a computes 2 raised to the given power
log2 :: (a ~ VecScalar d Float) => a -> a -> a 2 base logarithm
sqrt :: (a ~ VecScalar d Float) => a -> a -> a square root
inversesqrt :: (a ~ VecScalar d Float) => a -> a -> a 1 / square root
pow :: (a ~ VecScalar d Float) => a -> a -> a x raised to the y power

Common Functions

floor :: (a ~ VecScalar d Float) => a -> a the largest integer not larger than the value (component-wise)
trunc :: (a ~ VecScalar d Float) => a -> a the integer whose absolute value is largest but not larger than the absolute value of the value (component-wise)
round :: (a ~ VecScalar d Float) => a -> a the nearest integer to the value (component-wise)
roundEven :: (Num t, a ~ VecScalar d t) => a -> a -> a the nearest even integer to the value (component-wise)
ceil :: (Num t, a ~ VecScalar d t) => a -> a -> a the smallest integer not less than the value (component-wise)
fract :: (Num t, a ~ VecScalar d t) => a -> a -> a the fractional part of the value (component-wise)
modF :: (a ~ VecScalar d Float) => a -> (a, a) integer and fractional components (component-wise)
min :: (Num t, a ~ VecScalar d t) => a -> a -> a component-wise minimum
max :: (Num t, a ~ VecScalar d t) => a -> a -> a component-wise maximum
PrimMinS :: (Num t, a ~ VecScalar d t) => a -> t -> a minimum between (scalar, vector or matrix) and a scalar
PrimMaxS :: (Num t, a ~ VecScalar d t) => a -> t -> a maximum between (scalar, vector or matrix) and a scalar
isNan :: (a ~ VecScalar d Float, b ~ VecScalar d Bool) => a -> b True if the value is a number (component-wise)
isInf :: (a ~ VecScalar d Float, b ~ VecScalar d Bool) => a -> b True if the value is positive or negative infinity (component-wise)
abs :: (Signed t, a ~ VecScalar d t) => a -> a component-wise absolute value
sign :: (Signed t, a ~ VecScalar d t) => a -> a component-wise sign
clamp :: (Num t, a ~ VecScalar d t) => a -> a -> a -> a constrain a value to lie between two further values (component-wise)
clampS :: (Num t, a ~ VecScalar d t) => a -> t -> t -> a clamp with scalars
mix :: (a ~ VecScalar d Float) => a -> a -> a -> a linear interpolation between two values (component-wise)
mixS :: (a ~ VecScalar d Float) => a -> a -> Float -> a interpolate one scalar between (scalar, vector or matrix) values
mixB :: (a ~ VecScalar d Float) => a -> a -> VecScalar d Bool -> a True: select first value; False: select second value (component-wise)
step :: (a ~ Vec d Float) => a -> a -> a step function: 0.0 if the second value is less then the first; 1.0 otherwise (component-wise)
stepS :: (a ~ VecScalar d Float) => Float -> a -> a step with scalar
smoothstep :: (a ~ Vec d Float) => a -> a -> a -> a Hermite interpolation (component-wise)
smoothstepS :: (a ~ VecScalar d Float) => Float -> Float -> a -> a smoothstep with scalar

Fragment Processing Functions

dFdx (a ~ VecScalar d Float) => a -> a partial derivate
dFdy (a ~ VecScalar d Float) => a -> a partial derivate
fWidth (a ~ VecScalar d Float) => a -> a the sum of the absolute value of derivatives in x and y

Noise Functions

noise1 :: VecScalar d Float -> Float pseudo-random noise
noise2 :: VecScalar d Float -> Vec 2 Float pseudo-random noise
noise3 :: VecScalar d Float -> Vec 3 Float pseudo-random noise
noise4 :: VecScalar d Float -> Vec 4 Float pseudo-random noise

Bit-wise Functions

The functions operate component-wise and bit-wise.

bAnd :: (Integral t, a ~ VecScalar d t) => a -> a -> a logical AND
bOr :: (Integral t, a ~ VecScalar d t) => a -> a -> a logical OR
bXor :: (Integral t, a ~ VecScalar d t) => a -> a -> a logical XOR
bNot :: (Integral t, a ~ VecScalar d t) => a -> a logical NOT
bAndS :: (Integral t, a ~ VecScalar d t) => a -> t -> a bAnd with scalar
bOrS :: (Integral t, a ~ VecScalar d t) => a -> t -> a bOr with scalar
bXorS :: (Integral t, a ~ VecScalar d t) => a -> t -> a bXor with scalar

The functions operate component-wise.

shiftL :: (Integral t, a ~ VecScalar d t, b ~ VecScalar d Word) => a -> b -> a shift left
shiftR :: (Integral t, a ~ VecScalar d t, b ~ VecScalar d Word) => a -> b -> a shift right
shiftLS :: (Integral t, a ~ VecScalar d t) => a -> Word -> a shiftL with scalar
shiftRS :: (Integral t, a ~ VecScalar d t) => a -> Word -> a shiftR with scalar

Integer/Float Conversion Functions

The functions operate component-wise.

floatBitsToInt :: VecScalar d Float -> VecScalar d Int the encoding of a floating point value as an integer
floatBitsToWord :: VecScalar d Float -> VecScalar d Word the encoding of a floating point value as a word
intBitsToFloat :: VecScalar d Int -> VecScalar d Float inverse of floatBitsToInt
wordBitsToFloat :: VecScalar d Word -> VecScalar d Float inverse of floatBitsToWord

Common data structures

Homogeneous and heterogeneous lists (lists and tuples)

See the language specification

Maybe data type

Maybe has the standard definition:

data Maybe a = Nothing | Just a

Vector data type

data Vector (n :: Nat) t

A Vector n t is an vector with length n.
The difference between Vector and Vec is that there is no restriction on the length of a Vector.

Phase information
Currently Vector is an auxiliary phantom type used in the description of Fragment.

Special data structures

Geometric primitives

There are three type of primitives: triangles, lines and points.

data PrimitiveType
    = Triangle
    | Line
    | Point

A Primitive value is a container of 1, 2 or 3 values tagged with a PrimitiveType.

data Primitive a :: PrimitiveType -> Type where
    PrimPoint    :: a           -> Primitive a Point
    PrimLine     :: a -> a      -> Primitive a Line
    PrimTriangle :: a -> a -> a -> Primitive a Triangle

In LambdaCube3D, Primitive is always paramterized by a heterogeneous list whose elements are called attributes.

Primitive streams

PrimitiveStream is a central data type of GPUs:

type PrimitiveStream a p = [Primitive p a]

Mapping PrimitiveStreams

mapPrimitives :: (a -> b) -> PrimitiveStream p a -> PrimitiveStream p b
mapPrimitives f = map (mapPrimitive f)

mapPrimitive :: (a -> b) -> Primitive a p -> Primitive b p

You never need to call mapPrimitive directly; use mapPrimitives instead, which is compiled to runtime GPU code. (The compiled code will be part of the so-called vertex shader.)

Construction of PrimitiveStreams

There are two ways to construct a PrimitiveStream. The direct way is documented here, the other way is documented in Interface for inputs.

fetchArrays gets a heterogenous list of attribute lists. The atribute lists should have the same length.

fetchArrays :: HList x -> PrimitiveStream p (HList (map ListElem x))

type family ListElem a where ListElem [a] = a

Example usage:

    fetchArrays ([1.0, 2.0, 3.0], [True, False, True])
        :: PrimitiveStream Triangle (Float, Bool)

Restriction: If the primitive type is Triangle then the length of the lists should be divisible by 3; if the primitive type is Line then the length of the lists should be divisible by 2.
(The higher level API will detect misuse of fetchArrays statically.)

Fragments

A Fragment n t value is an n-vector of Maybe (SimpleFragment t) values. Maybe is used because simple fragments may be filtered out with the filterFragments function (see later).

type Fragment n t = Vector n (Maybe (SimpleFragment t))

A SimpleFragment value is an attribute tuple plus a viewport coordinate with depth value:

data SimpleFragment t = SimpleFragment
    { sFragmentCoords   :: Vec 3 Float  -- x, y, depth
    , sFragmentValue    :: t
    }

Basic functions on fragments:

customizeDepth :: (a -> Float) -> Fragment n a -> Fragment n a
filterFragment :: (a -> Bool)  -> Fragment n a -> Fragment n a
mapFragment    :: (a -> b)     -> Fragment n a -> Fragment n b

Fragment streams

FragmentStream is a list of fragments:

type FragmentStream n t  = [Fragment n t]

Operations on FragmentStreams:

customizeDepths :: (a -> Float) -> FragmentStream n a -> FragmentStream n a
customizeDepths f = map (customizeDepth f)

filterFragments :: (a -> Bool) -> (FragmentStream n a) -> (FragmentStream n a)
filterFragments p = map (filterFragment p)

mapFragments :: (a -> b) -> FragmentStream n a -> FragmentStream n b
mapFragments f = map (mapFragment f)

Phase information
customizeDepths, filterFragments and mapFragments are compiled to runtime GPU code.
The other operations are not accessible in any phase.

Images

Image kinds

There are different kind of images depending on the role the image plays:

data ImageKind
    = Color (c :: Type)     -- the image stores `c`-values
    | Depth                 -- the image stores depth values (`Float`s)
    | Stencil               -- the image stores stencil values (`Int`s)

The type of the stored values is calculated by imageType:

imageType :: ImageKind -> Type
imageType (Color c) = c
imageType Depth     = 'Float
imageType Stencil   = 'Int

Image type

An Image n t is n times a rectangle of imageType t-pixels:

type Image (n :: Nat) (t :: ImageKind) = Vector n [[imageType t]]

The n parameter is the so called layer count, which count the number of layers. Usually it is 1.

The layer count of an image:

type family ImageLC a :: Nat where ImageLC (Image n t) = n

Functions which construct clear images:

ColorImage   :: (Num t, color ~ VecScalar d t) => color  -> Image a (Color color)
DepthImage   :: Float  -> Image n Depth
StencilImage :: Int    -> Image n Stencil

Some wrapper functions for creating 1-layered clear images:

emptyDepthImage = DepthImage @1
emptyColorImage = ColorImage @1

Other image construction functions (texture support):

PrjImage            :: FrameBuffer 1 a -> Image 1 a
PrjImageColor       :: FrameBuffer 1 (Depth Float, Color (Vec 4 Float)) -> Image 1 (Color (Vec 4 Float))

Phase information
ColorImage, DepthImage, StencilImage, PrjImage and PrjImageColor are compiled to runtime GPU code.
The other operations are not accessible in any phase.

Framebuffers

A FrameBuffer n t is a tuple of images:

type FrameBuffer (n :: Nat) (t :: [ImageKind]) = HList (map (Image n) t)

Construction of framebuffers

One can construct a framebuffer from images if all have the same layer count:

imageFrame :: forall (a :: [Type]) . (sameLayerCounts a) => HList a -> FrameBuffer (ImageLC (head a)) (map GetImageKind a)

sameLayerCounts a = allSame (map 'ImageLC a)

allSame :: [a] -> Type
allSame [] = 'Unit
allSame [x] = 'Unit
allSame (x: y: xs) = 'T2 (x ~ y) (allSame (y:xs))

Rasterization

Rasterization turns a Primitive value into a list of Fragment values.
For this, a rasterization context is needed, and also information how interpolation should be done.

Rasterization contexts

There are different rasterization contexts for each primitive types:

data RasterContext a :: PrimitiveType -> Type where
    TriangleCtx  :: CullMode -> PolygonMode a -> PolygonOffset -> ProvokingVertex -> RasterContext a Triangle
    LineCtx      :: Float -> ProvokingVertex                                      -> RasterContext a Line
    PointCtx     :: PointSize a -> Float -> PointSpriteCoordOrigin                -> RasterContext a Point

Triangles can be discarded based on their apparent facing, a process known as Face Culling:

data CullMode
    = CullFront     -- triangle faces whose vertices appear counter-clockwise are culled
    | CullBack      -- triangle faces whose vertices appear clockwise are culled
    | CullNone      -- no culling

Triangles can be rendered in three ways according to PolygonMode values. Note that in WebGL triangles are allways filled regardless of PolygonMode values.

data PolygonMode a
    = PolygonFill                   -- filled triangles (mostly used)
    | PolygonPoint (PointSize a)    -- only triangle vertices are rendered as squares with the given size
    | PolygonLine Float             -- only triangle edges are rendered with the given width

PolygonOffset is used to eliminate the visual unpleasantness when you want to highlight the edges of a solid object. See glPolygonOffset.

data PolygonOffset
    = NoOffset              -- no offset
    | Offset Float Float    -- how to tweak depth values: *factor* and *units*

ProvokingVertex values are used only in case of Flat interpolation (see later).

data ProvokingVertex
    = LastVertex            -- the value of the last vertex is used in flat interpolation
    | FirstVertex           -- the value of the first vertex is used in flat interpolation

Points will be rendered as squares.
The square sides’ size is can given in two ways with PointSize data:

data PointSize a
    = PointSize Float                   -- static point size
    | ProgramPointSize (a -> Float)     -- dynamic point size which depends on attributes

Point are rendered as squares which can be texured. PointSpriteCoordOrigin tells where is the origin for the texure.

data PointSpriteCoordOrigin
    = LowerLeft       -- texture origin is lower left corner
    | UpperLeft       -- texture origin is lower left corner

Interpolation types

During rasterization, attribute values are interpolated between the vertices of triangles and lines.

OpenGL supports three types of interpolation:

data Interpolated t where
  Flat          ::                 Interpolated t   -- no interpolation
  Smooth        :: (Floating t) => Interpolated t   -- smooth interpolation
  NoPerspective :: (Floating t) => Interpolated t   -- no perspective correction

In case of Flat interpolation, either the value of the first vertex or the value of the last vertex is chosen. The ProvokingVertex value of the rasterization context tells which one to choose (see above).

The rasterizePrimitive function

rasterize turns a Primitive into a FragmentStream, given a rasterization contex and info for interpolation:

rasterizePrimitive
    :: a ~ Vec 4 Float: b
    => HList (map Interpolated b)                -- tuple of Smooth & Flat
    -> RasterContext (HList a) pr
    -> Primitive (HList a) pr
    -> FragmentStream 1 (HList b)

Notes:

Rasterization of primitive streams

Always use rasterizePrimitives instead of rasterize becase the former is compiled to runtime GPU code:

rasterizePrimitives
    :: a ~ Vec 4 Float: b
    => HList (map Interpolated b)              -- tuple of Smooth & Flat
    -> RasterContext (HList a) pr
    -> PrimitiveStream pr (HList a)
    -> FragmentStream 1 (HList b)
rasterizePrimitives ctx is s = concat (map (rasterize is ctx) s)

Accumulation

Accumulation combines overlaying fragments. How to combine two overlaying fragments is described by an accumulation context.

The result of the accumulation is a framebuffer (a tuple of images).

Accumulation contexts

An accumulation context is a tuple which contains one fragment operation for each fragment attribute.

There are three kind of fragment operations depending of the image kind:

data FragmentOperation :: ImageKind -> Type where
  ColorOp   :: Num c
            => Blending c           -- blending function
            -> VecScalar d Bool     -- blending filter
            -> FragmentOperation (Color (VecScalar d c))
  DepthOp   :: ComparisonFunction
            -> Bool
            -> FragmentOperation Depth
  StencilOp :: StencilTests
            -> StencilOps
            -> StencilOps
            -> FragmentOperation Stencil

The ImageKind of a fragment operation:

type family FragmentOperationKind a :: ImageKind where
    FragmentOperationKind (FragmentOperation x) = x

Blending is combination of color values.

data Blending :: Type -> Type where
  NoBlending    ::                                   Blending t
  BlendLogicOp  :: (Integral t) => LogicOperation -> Blending t
  Blend         :: (BlendEquation, BlendEquation)
                -> ((BlendingFactor, BlendingFactor), (BlendingFactor, BlendingFactor))
                -> Vec 4 Float
                -> Blending Float
data LogicOperation
    = Clear
    | And
    | AndReverse
    | Copy
    | AndInverted
    | Noop
    | Xor
    | Or
    | Nor
    | Equiv
    | Invert
    | OrReverse
    | CopyInverted
    | OrInverted
    | Nand
    | Set
data BlendingFactor
    = ZeroBF
    | OneBF
    | SrcColor
    | OneMinusSrcColor
    | DstColor
    | OneMinusDstColor
    | SrcAlpha
    | OneMinusSrcAlpha
    | DstAlpha
    | OneMinusDstAlpha
    | ConstantColor
    | OneMinusConstantColor
    | ConstantAlpha
    | OneMinusConstantAlpha
    | SrcAlphaSaturate
data BlendEquation
    = FuncAdd
    | FuncSubtract
    | FuncReverseSubtract
    | Min
    | Max

Comparison functions for depth values.

data ComparisonFunction
    = Never
    | Less
    | Equal
    | Lequal
    | Greater
    | Notequal
    | Gequal
    | Always

Stencil operations.

data StencilOperation
    = OpZero
    | OpKeep
    | OpReplace
    | OpIncr
    | OpIncrWrap
    | OpDecr
    | OpDecrWrap
    | OpInvert

Accumulation function

The overlay function overlays the fragments of a fragment streams on the given background images:

overlay
    :: forall (n :: Nat) (c :: [Type])
    . b ~ map FragmentOperationKind c
    => FrameBuffer n b                            -- backround images
    -> ( HList c                                  -- fragment operations (accumulation context)
       , FragmentStream n (HList (imageType' b))  -- fragment steam
       )
    -> FrameBuffer n b                            -- result images

imageType' :: [ImageKind] -> [Type]
imageType' (Depth: x) = map imageType x 
imageType' x = map imageType x 

Notes:

accumulateWith just pairs an accumulation context with a fragment stream:

accumulateWith ctx x = (ctx, x)

Getting inputs

The input of a pipeline consists of primitive streams, uniforms and textures.

Uniforms and textures

Uniforms can be imported by their name:

Uniform   :: String -> t

Note that currently the type of the uniform is not statically checked.

A texture is a uniform with type Texture. Currently textures are treated specially in LambdaCube 3D but this is planned to be changed.

Primitive streams

A primitive stream can be imported by fetch:

fetch
    :: forall a t
    . String            -- name of the primitive stream
    -> HList t          -- tuple of attribute names, given by `Attribute`
    -> PrimitiveStream a (HList t)

Attribute names can be given by Attribute:

Attribute :: String -> t

Note that currently the type of the attributes are not statically checked.

Interface for outputs

Output is an existential type which wraps a framebuffer.

data Output where
  ScreenOut           :: FrameBuffer a b -> Output
renderFrame = ScreenOut