Home

Awesome

shadergraph

Functional GLSL linker

Shader Graph

ShaderGraph is a library for linking together GLSL snippets into stand-alone shaders. It is mainly meant to build complicated shaders 100% programmatically. But it can also act as the back-end to a live graph-based shader editor, as its graph model is persistent.

Snippets can be simple one-liners, or multi-function source files. Snippets can also declare callbacks, as functions without bodies, linked in from elsewhere. This allows complicated execution flows to be built very easily.

vec3 getColor();
void main() {
  gl_FragColor = vec4(getColor(), 1.0);
}

ShaderGraph is designed to play well with Three.js, but does not depend on it. It merely follows the same code/object conventions.

There is no editing UI included, only a way to display a graph as an HTML/CSS/SVG diagram.


Live Examples

ShaderGraph

ShaderGraph 2 drives all shaders in MathBox² (in development). For more info, see the articles on acko.net:

Here's a real-world vertex shader for a line primitive, sampling color and position from two textures:

<a href="http://acko.net/files/shadergraph2/vertex-color-shader-tidy.png"><img src="http://acko.net/files/shadergraph2/vertex-color-shader-tidy.png"></a>

<a href="http://acko.net/files/mathbox2/iframe-readyornot.html"><img src="http://acko.net/files/mathbox2/cover2.jpg" alt="MathBox 2 - Audio Visualizer example"></a>

You can also use ShaderGraph's graph visualizer directly for other purposes:

Basic Use

Install via npm:

npm install shadergraph

Include build/shadergraph.js.

To use ShaderGraph, you initialize it once with a given snippet library. A snippet library is either a dictionary of named snippets, or a fetch function.

// Dynamic fetch
var fetch = function (name) {
  return "...";
};

// Static fetch
var fetch = {
  getColor: "...",
  setColor: "...",
  getRampColor: "...",
};

var shadergraph = ShaderGraph.load(fetch);

You can use the chainable Factory API to build graphs. It's a smart wrapper around a partially built graph. It allows you to make splits and joins, hook up callbacks via requires, import other factories, etc.

Instead of including snippets by name, you can also pass in GLSL code directly to .pipe(…) and .require(…) regardless of whether you are using a fetch function/library or not.

Snippets are instanced by default, letting you bind unique uniforms to specific snippets in the chain:

Uniform example

// Prepare new shader
var shader = shadergraph.shader();

// Prepare uniform (three.js style)
var uniforms = {
  diffuseColor: { type: "v3", value: { x: 0.5, y: 0.75, z: 1.0 } },
};

// Build shader graph
shader
  // Require a callback
  .require("getRampColor")

  // Build two-step chain that uses the callback and the uniform
  .pipe("getColor", uniforms)
  .pipe("setColor");

var program = shader.link();

Instancing behavior can be configured globally or per shader (see below).

Materials

ShaderGraph also includes a material helper, to build a vertex/fragment shader simultaneously:

Material example

// Prepare new material (vertex + fragment shader)
var material = shadergraph.material();

// Build vertex shader graph
material.vertex.pipe("vertex");

// Build fragment shader graph
material.fragment.pipe("getColor").pipe("setColor");

// Link both shaders and combine into a three.js style material
var program = material.link();

The returned program object is compatible with Three.js' ShaderMaterial objects.

Caveats


Reference

Constructor

var fetch = function (name) { return … };
var fetch = { name: "...", name: "..." };
var config = {
  globalUniforms:   false, // Make uniforms   global
  globalVaryings:   true,  // Make varyings   global
  globalAttributes: true,  // Make attributes global
  globals:          [],    // Make specific symbols global
  autoInspect:      false, // Pop-up a graph inspector if compilation fails
}
shadergraph = ShaderGraph.load(fetch, config);

ShaderGraph

Factory

Graph

Material

Manual Use

If you want to build graphs by hand instead of with factories, this is possible, but not as nice. You will need to construct objects and inject a few dependencies. Use the Factory API as a guide.

The underlying namespaces are exposed as ShaderGraph.Graph, ShaderGraph.Block, … Block and its subclasses are the logical pieces of the shader. Each block has a Node associated with it that lives in the Graph and contains a set of Outlets. Connections can be made node-to-node with node.connect(node) (auto-matching by name and type), or outlet-to-outlet with outlet.connect(outlet).

To compile Graphs created without a factory, you will need to call .compile() or .link() on the graph's tail block directly.


Steven Wittens - http://acko.net/