mischief-render
Safe HaskellNone
LanguageGHC2024

Mischief.Render.Tutorial.Shaders

Description

Synopsis

    2.2. Shaders

    This chapter will teach you about writing custom shaders in the Shader monad.

    The Shmonad

    The shader monad is a state monad in which you build custom shader code which will then be parsed into actual code. When writing shaders, it is recommended to do the following imports:

    import Prelude()
    import Mischief.Materials
    

    Prelude needs to be hidden, as shader code uses its own definition of primitive types and operations. See for instance, Float vs Float and + vs. +.

    Types

    When working with shaders, there are 3 primitive types:

    Each can be built with a literal. For instance, this is a shader that returns 5:

    five :: Shader a Int
    five = pure 5
    

    There are vecs of between 2 and 4 elements. Each size having its own constructor: vec2, vec3, vec4.

    foo :: Vec3 Float
    foo = vec3 3.4 0.1 (-5)
    

    There are also arrays, which can be of vecs or primitives.

    bar :: Array (Vec2 Uint)
    bar = array [vec2 5 0, vec2 1 4, vec2 10 3]
    

    Arrays can be indexed into using !!:

    let x = bar !! 2
    

    Finally, there are matrices:

    baz :: Mat 2 3
    baz = mat (vec3 5 0 4, vec3 1 1 1)
    

    You can cast between different primitive types, or between vecs of the same size, using cast.

    Operations

    Algebric operators are defined using a convention inspired by Linear.

    Two elements of the same type can be multiplied using *.

    let a :: Float = 2 * 3
    

    A scalar (left) can be multiplied with a vector (right) using *^, and vice versa for ^*.

    let a :: Vec2 Float = 2 *^ vec2 5 2
    

    A matrix (left) can be multiplied with a vector (right) using !*^, or with a scalar (right) using !*. Vice versa for ^*! and *!.

    The same goes for other operators, such as /, +, -, %.

    Functions

    Shader functions are defined according to the [wgpu standard](https:/www.w3.orgTRWGSL] in Mischief.Shader.Functions.

    If a function you need isn't yet defined by Mischief, you can bind it yourself using unsafeCall. For instance, assuming atan2 wasn't defined, you could call it like this:

    let x :: Vec2 Float = vec2 5 2
    let y :: Vec2 Float = vec2 1 2
    let z :: Vec2 Float = unsafeCall "atan2" [Param x, Param y]
    

    But be careful! This removes all typechecking. If you call a function with the wrong parameters it will panic.

    Next Chapter: Materials