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:
- Compile time CPU
- Runtime CPU
- Runtime GPU
The runtime CPU and runtime GPU phases are interleaved.
Design principles:
- Every reduction step is done in the runtime CPU phase by default.
- 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. - As an optimization, some reductions are done at compile time CPU instead of runtime CPU.
- 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 PrimitiveStream
s
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 PrimitiveStream
s
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 FragmentStream
s:
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:
- The first attribute of the primitive stream should have type
Vec 4 Float
.
This attribute is omitted in the result fragment stream. - One
Interpolated
value is needed for each fragment attribute for rasterization.
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:
- No fragment attribute is needed for overlaying depth images: the depth value is taken from the fragment viewport coordinates.
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