LYGIA Shader Library
Tired of searching, porting and/or reimplementing the same functions over and over? LYGIA is a shader library of reusable functions that can be include easily on your projects. LYGIA is very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.
How to use it?
In your shader #include
the functions you need:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
#include "lygia/space/ratio.glsl"
#include "lygia/math/decimate.glsl"
#include "lygia/draw/circle.glsl"
void main(void) {
vec3 color = vec3(0.0);
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st = ratio(st, u_resolution);
color = vec3(st.x,st.y,abs(sin(u_time)));
color = decimate(color, 20.);
color += circle(st, .5, .1);
gl_FragColor = vec4(color, 1.0);
}
LYGIA Locally
If you are working locally on an ecosystem that can resolve #include
dependencies, just clone LYGIA in your project relative to the shader you are loading:
git clone https://github.com/patriciogonzalezvivo/lygia.git
or as a submodule:
git submodule add https://github.com/patriciogonzalezvivo/lygia.git
LYGIA on the cloud
If you are working on a cloud platform probably you want to resolve the dependencies without needing to install anything. Just add a link to https://lygia.xyz/resolve.js
(JS) or https://lygia.xyz/resolve.esm.js
(ES6 module):
<!-- as a JavaScript source -->
<script src="https://lygia.xyz/resolve.js"></script>
<!-- Or as a ES6 module -->
<script type="module">
import resolveLygia from "https://lygia.xyz/resolve.esm.js"
</script>
To then resolve the dependencies by passing a string
or strings[]
to resolveLygia()
or resolveLygiaAsync()
:
// 1. FIRST
// Sync resolver, one include at a time
vertSource = resolveLygia(vertSource);
fragSource = resolveLygia(fragSource);
// OR.
// ASync resolver, all includes in parallel calls
vertSource = resolveLygiaAsync(vertSource);
fragSource = resolveLygiaAsync(fragSource);
// 2. SECOND
// Use the resolved source code
shdr = createShader(vertSource, fragSource);
Integrations examples
Learn more about how to use it from this examples:
- 2D examples for Processing (GLSL)
- 2D/3D examples for P5.js (GLSL)
- 2D examples for Three.js + React (GLSL) by Eduard Fossas
- 2D examples for Three.js (GLSL)
- 3D examples for Three.js (GLSL) by Guido Schmidt
- 2D examples for OpenFrameworks (GLSL)
- 2D/3D examples for Unity3D (HLSL)
- 2D examples for Touch Designer (GLSL) (dynamic resolver) by Leith Ben Abdessalem
- 2D examples for Touch Designer (GLSL) (static resolver) by Victor Saz
- 2D examples on Observable Notebook (GLSL) by Radames Ajna
- Figma's noise&texture plugin by Rogie King. You will need to go to the "Custom" tab on the plugin to edit shaders and load LYGIA modules
- 3D example on Irmf by Glenn Lewis
- 2D/3D examples on GlslViewer (GLSL)
- 2D examples on Ossia by Jean-Michaël Celerier
For more information, guidance or feedback about using LYGIA, join #Lygia channel on shader.zone discord.
How is it organized?
The functions are divided in different categories:
math/
: general math functions and constants likePI
,SqrtLength()
, etc.space/
: general spatial operations likescale()
,rotate()
, etc.color/
: general color operations likeluma()
,saturation()
, blend modes, palettes, color space conversion and tonemaps.animation/
: animation operations, like easinggenerative/
: generative functions likerandom()
,noise()
, etc.sdf/
: signed distance field functions.draw/
: drawing functions likedigits()
,stroke()
,fill
, etc/.sample/
: sample operationsfilter/
: typical filter operations like different kind of blurs, mean and median filters.distort/
: distort sampling operationssimulate/
: simulate sampling operationslighting/
: different lighting models and functions for foward/deferred/raymarching renderinggeometry/
: operation related to geometries. Like intersections and AABB accelerating structures.morphological/
: morphological filters like: dilation, erosion, alpha and poisson fill.
Flexible how?
There are some functions whose behaviour can be changed using the #defines
keyword before including it. For example, gaussian blurs are usually are done in two passes. By default, these are performed on their 1D version, but in the case you are interested on using a 2D kernel, all in the same pass, you will need to add the GAUSSIANBLUR_2D
keyword this way:
#define GAUSSIANBLUR_2D
#include "filter/gaussianBlur.glsl"
void main(void) {
...
vec2 pixel = 1./u_resolution;
color = gaussianBlur(u_tex0, uv, pixel, 9);
...
}
Design Principles
- It relies on
#include "path/to/file.*lsl"
which is defined by Khronos GLSL standard and requires a tipical C-like pre-compiler MACRO which is easy to implement with just basic string operations to resolve dependencies.
Here you can find some implementations on different languages:
-
C#:
. GLSLIncludes a small utility to add the include feature to glsl by z0rg
-
C++:
. VERA's routines for resolving GLSL dependencies.
-
Python:
-
JavaScript:
. vanilla JS (online resolver) This small file brings
resolveLygia()
which takes astring
orstring[]
and parse it solving all the#include
dependencies into a singlestring
you can load on your shaders. npm module (online resolver) by Eduardo Fossas; vite glsl plugin (local bundle) by Ustym Ukhman. Imports
.glsl
local dependencies, or load inline shaders through vite. esbuild glsl plugin (local bundle) by Ricardo Matias. Import local
.glsl
dependencies through esbuild. webpack glsl plugin (local bundle) by Ryan Grieve that import local
.glsl
dependencies through webpack.
- It's very granular. One function per file. The file and the function share the same name, namely:
myFunc.glsl
containsmyFunct()
. There are some files that just include a collection of files inside a folder with the same name. For example:
color/blend.glsl
// which includes
color/blend/*.glsl
- It's multi language. Right now most of is GLSL (
*.glsl
) and HLSL (*.hlsl
), but we are slowly extending to WGSL (*.wgsl
), CUDA (*.cuh
) and Metal (*.msl
).
math/mirror.glsl
math/mirror.hlsl
math/mirror.wgsl
math/mirror.msl
math/mirror.cuh
- Self documented. Each file contains a structured comment (in YAML) at the top of the file. This one contains the name of the original author, description, use and
#define
options
/*
original_author: <FULL NAME>
description: [DESCRIPTION + URL]
use: <vec2> myFunc(<vec2> st, <float> x [, <float> y])
options:
- MYFUNC_TYPE
- MYFUNC_SAMPLER_FNC()
*/
- Prevents name collisions by using the following pattern where
FNC_
is followed with the function name:
#ifndef FNC_MYFUNC
#define FNC_MYFUNC
float myFunc(float in) {
return in;
}
#endif
- Templating capabilities through
#defines
. Probably the most frequent use is templating the sampling function for reusability. The#define
options start with the name of the function, in this exampleMYFUNC_
. They are added asoptions:
in the header.
#ifndef MYFUNC_TYPE
#define MYFUNC_TYPE vec4
#endif
#ifndef MYFUNC_SAMPLER_FNC
#define MYFUNC_SAMPLER_FNC(TEX, UV) texture2D(TEX, UV)
#endif
#ifndef FNC_MYFUNC
#define FNC_MYFUNC
MYFUNC_TYPE myFunc(sampler2D tex, vec2 st) {
return MYFUNC_SAMPLER_FNC(tex, st);
}
#endif
- Function Overloading. Arguments are arranged in such a way that optional elements are at the back. When possible sort them according their memory size (except textures that reamin at the top). Ex.:
sampler2D, mat4, mat3, mat2, vec4, vec3, vec2, float, ivec4, ivec3, ivec2, int, bool
/*
...
use: myFunc(<vec2> st, <vec2|float> x[, <float> y])
*/
#ifndef FNC_MYFUNC
#define FNC_MYFUNC
vec2 myFunc(vec2 st, vec2 x) {
return st * x;
}
vec2 myFunc(vec2 st, float x) {
return st * x;
}
vec2 myFunc(vec2 st, float x, float y) {
return st * vec2(x, y);
}
#endif
Contributions
LYGIA have a long way to go. Your support will be appreciated and rewarded (all contributors are automatically added to the commercial license ). This support can take multiple forms:
- fixing bugs!
- expanding the crosscompatibility between languages GLSL/HLSL/MSL/WGSL/CUDA
- contributing new functions
- adding new examples and integrations for new enviroments like: GoDot, ISF, MaxMSP, etc.
- through sponsorships
License
LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.
Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).
It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.
Exceptions:
color/mixBox.glsl
andcolor/mixBox.hlsl
it's copyrighted by Secret Weapons with their own non-commercial license. This functions also require a LUT texture wich is provided for research and evaluation purposes, if you wish to obtain it together with a commercial license, please contact them at mixbox@scrtwpns.com
Credits
Created and mantain by Patricio Gonzalez Vivo( Mastodon | Twitter | Instagram | GitHub ) and every direct or indirect contributors to the GitHub. This library has been built over years, and in many cases on top of the work of brillant and generous people like: Inigo Quiles, Morgan McGuire, Alan Wolfe, Hugh Kennedy, Matt DesLauriers and many others.
Get the latest news and releases
Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository