moonshine

repository·master·Indexed 20 days ago

https://github.com/vrld/moonshine

A library of chainable post-processing shaders for the LÖVE (Love2D) game engine. It enables developers to stack visual effects such as blur, film grain, CRT distortion, glow, and vignette onto game rendering using a sequential chain system. The library includes a variety of built-in effects and provides a framework for creating custom effects with support for double buffering and multi-pass shaders.

Tokens
2.5K
Snippets
5
Records
9
Agent score
22%

What's inside moonshine

  1. How chains and effects work in moonshine

    master

    The core concept of moonshine is the chain. A chain is a sequence of one or more effects applied to your drawing commands. Effects are applied sequentially: an effect later in the chain is applied to the result of the effects that came before it.

    Creating a chain

    You can create a chain using moonshine.chain(effect) or the alias moonshine(effect). You can append new effects using .chain(another_effect) or .next().

    Applying effects to drawing

    To apply the chain to specific objects, wrap your drawing code in a function and pass it to the chain using chain.draw(func, ...) or the alias chain(func, ...). Only things drawn inside the function will be affected by the chain.

    Note: Some effects (like filmgrain) draw on the whole screen. If you use such an effect, it may cover objects drawn outside the chain.draw block, even if those objects aren't technically part of the chain's processing.

    -- Setup
    effect = moonshine(moonshine.effects.filmgrain)
                      .chain(moonshine.effects.vignette)
    
    -- Usage in draw loop
    function love.draw()
        effect(function()
          love.graphics.rectangle("fill", 300, 200, 200, 200)
        end)
    end
  2. Using buffers in custom draw functions

    master

    Moonshine uses double buffering to render effects. When implementing a custom draw function, you can access and swap these buffers using the buffer() function.

    Swapping Buffers

    To swap the front and back buffers and retrieve both, use: front, back = buffer()

    Multi-pass Shaders

    If your custom draw function is only needed to perform multiple shader passes, use the draw_shader(buffer, shader) helper. This function draws the current front buffer to the back buffer using the provided shader. This is useful for effects like boxblur that require multiple iterations.

    -- Accessing buffers in a draw function
    function my_effect_draw(buffer)
        front, back = buffer()
        -- perform drawing logic using front and back
    end
    
    -- Performing a shader pass
    function my_effect_draw(buffer)
        draw_shader(buffer, my_shader)
    end
  3. Advanced buffer management for complex effects

    master

    While Moonshine is optimized for two buffers, you can implement more complex logic if required (e.g., for effects like glow).

    Warning: If you use more than two buffers or perform complex custom drawing, you are responsible for ensuring that the blend mode and the order of the back and front buffers are identical to the state before and after your custom draw function execution.

  4. Configure effect parameters

    master

    Effects in moonshine are parametrized to change their appearance. You can set parameters in two ways:

    Individual parameter setting

    Access the effect by name through the chain, then specify the parameter: chain.<effect_name>.<parameter_name> = <value>

    Bulk parameter setting

    To initialize multiple parameters at once, use the parameters (or params or settings) key with a nested table. This only updates the keys you provide and leaves others untouched.

    chain.parameters = {
      glow = {strength = 10},
      crt = {distortionFactor = {1.06, 1.065}},
    }
    chain.glow.strength = 10
    chain.crt.distortionFactor = {1.06, 1.065}
  5. Manage effect state and canvas size

    master

    Temporarily disabling effects

    You can turn effects on or off without removing them from the chain using chain.disable(names...) and chain.enable(names...).

    effect.disable("boxblur", "filmgrain")
    effect.enable("filmgrain")

    Resizing the internal canvas

    If your window size changes, call chain.resize(width, height) to update the internal canvas. It is recommended to call this outside of chain.draw().

    You can also specify an initial canvas size when creating the chain:

    effect = moonshine(400, 300, moonshine.effects.vignette)
  6. Writing custom effects in Moonshine

    master

    An effect is a function that returns a moonshine.Effect{} object. To create a valid effect, you must specify at least a name and either a shader or a draw function.

    Effect Structure

    • name: A unique identifier for the effect.
    • shader: A shader used for the effect pass.
    • draw: A custom function for complex drawing logic.
    • setters: (Optional) A table of functions used to update effect parameters.
    • defaults: (Optional) A table containing the default values for the parameters defined in setters.

    Moonshine handles canvas management and restoring defaults automatically. When an effect is instantiated, the values in defaults are applied.

    -- Example structure based on the colorgradesimple pattern
    function my_custom_effect()
        return {
            name = 'my_effect',
            shader = my_shader,
            defaults = {
                intensity = 0.5
            },
            setters = {
                set_intensity = function(effect, value) 
                    -- logic to set parameter
                end
            }
        }
    end
  7. Install and setup moonshine

    master

    To use moonshine in your LÖVE project, clone the repository into your game folder:

    git clone https://github.com/vrld/moonshine.git

    Then, require the library in your main.lua or wherever you load your libraries:

    local moonshine = require 'moonshine'
  8. Reference: Effect parameters

    master

    Below are the parameter definitions for the available effects.

    boxblur

    moonshine.effects.boxblur

    • radius: number or table of numbers (Default: {3,3})
    • radius_x: number (Default: 3)
    • radius_y: number (Default: 3)

    chromasep

    moonshine.effects.chromasep

    • angle: number in radians (Default: 0)
    • radius: number (Default: 0)

    colorgradesimple

    moonshine.effects.colorgradesimple

    • factors: table of numbers (Default: {1,1,1})

    crt

    moonshine.effects.crt

    • distortionFactor: table of numbers (Default: {1.06, 1.065})
    • x: number (Default: 1.06)
    • y: number (Default: 1.065)
    • scaleFactor: number or table of numbers (Default: {1,1})
    • feather: number (Default: 0.02)

    desaturate

    moonshine.effects.desaturate

    • tint: color / table of numbers (Default: {255,255,255})
    • strength: number between 0 and 1 (Default: 0.5)

    dmg

    moonshine.effects.dmg

    • palette: number, string, or table of tables of numbers (Default: "default")
      • Available strings: "default", "dark_yellow", "light_yellow", "green", "greyscale", "stark_bw", "pocket".
      • Custom format: {{R,G,B}, {R,G,B}, {R,G,B}, {R,G,B}} where values are 0-255.

    fastgaussianblur

    moonshine.effects.fastgaussianblur

    • taps: odd number >= 3 (Default: 7)
    • offset: number (Default: 1)
    • sigma: number (Default: -1)

    filmgrain

    moonshine.effects.filmgrain

    • opacity: number (Default: 0.3)
    • size: number (Default: 1)

    gaussianblur

    moonshine.effects.gaussianblur

    • sigma: number (Default: 1)

    glow

    moonshine.effects.glow

    • min_luma: number between 0 and 1 (Default: 0.7)
    • strength: number >= 0 (Default: 5)

    godsray

    moonshine.effects.godsray

    • exposire: number between 0 and 1 (Default: 0.5)
    • decay: number between 0 and 1 (Default: 0.95)
    • density: number between 0 and 1 (Default: 0.05)
    • weight: number between 0 and 1 (Default: 0.5)
    • light_position: table of two numbers (Default: {0.5, 0.5})
    • light_x: number (Default: 0.5)
    • light_y: number (Default: 0.5)
    • samples: number >= 1 (Default: 70)

    pixelate

    moonshine.effects.pixelate

    • size: number or table of two numbers (Default: {5,5})
    • feedback: number between 0 and 1 (Default: 0)

    posterize

    moonshine.effects.posterize

    • num_bands: number >= 1 (Default: 3)

    scanlines

    moonshine.effects.scanlines

    • width: number (Default: 2)
    • frequency: number (Default: screen-height)
    • phase: number (Default: 0)
    • thickness: number (Default: 1)
    • opacity: number (Default: 1)
    • color: color / table of numbers (Default: {0,0,0})

    sketch

    moonshine.effects.sketch

    • amp: number (Default: 0.0007)
    • center: table of numbers (Default: {0,0})

    vignette

    moonshine.effects.vignette

    • radius: number > 0 (Default: 0.8)
    • softness: number > 0 (Default: 0.5)
    • opacity: number > 0 (Default: 0.5)
    • color: color / table of numbers (Default: {0,0,0})

    fog

    moonshine.effects.fog

    • fog_color: color/table of numbers (Default: {0.35, 0.48, 0.95})
    • octaves: number > 0 (Default: 4)
    • speed: vec2/table of numbers (Default: {0.5, 0.5})
  9. Reference: List of available effects

    master

    Moonshine includes the following built-in effects:

    • boxblur: simple blurring
    • chromasep: cheap/fake chromatic aberration
    • colorgradesimple: weighting of color channels
    • crt: crt/barrel distortion
    • desaturate: desaturation and tinting
    • dmg: Gameboy and other four color palettes
    • fastgaussianblur: faster Gaussian blurring
    • filmgrain: image noise
    • gaussianblur: Gaussian blurring
    • glow: light bloom
    • godsray: light scattering
    • pixelate: sub-sampling
    • posterize: restrict number of colors
    • scanlines: horizontal lines
    • sketch: simulate pencil drawings
    • vignette: shadow in the corners
    • fog: fog effect