1.2.1 • Published 11 months ago

@plutotcool/glsl-bundler v1.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

glsl-bundler

test build release version types

Functional regex-based bundling tools for glsl. Run both on node and the browser.

Live demo

Install

yarn add @plutotcool/glsl-bundler

Usage

Common examples

Load a shader as a string and resolve #include directives:

import { load } from '@plutotcool/glsl-bundler'

await load(`./fragment.glsl`, import.meta.url)

Edit a shader:

import { define } from '@plutotcool/glsl-bundler'

const source = `
  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }
`

define(source, 'PI', 3.141592653589793)

Transpile a shader:

import { transpile } from '@plutotcool/glsl-bundler'

const source = `#version es 300
  uniform diffuse sampler2D;

  in vec2 vUv;
  out vec4 FragColor;

  void main() {
    FragColor = texture(diffuse, vUv);
  }
`

transpile(source)

Minify a shader:

import { minify } from '@plutotcool/glsl-bundler'

minify(`
  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }
`)

Combine loading, editing, transpiling and minifying into a bundle function:

import {
  bundler,
  loader,
  writer,
  transpiler,
  minifier
} from '@plutotcool/glsl-bundler'

const bundle = bundler([
  loader(import.meta.url),
  writer.define('PI', 3.141592653589793),
  transpiler(),
  minifier()
])

await bundle(`
  #include ./pi.glsl
  #include ./rad2deg.glsl

  void main() {
    float turn = rad2deg(PI * 2.0);
  }
`)

Bundler

The bundler factory creates a function that transforms glsl source code given an array of transform functions:

import { bundler } from '@plutotcool/glsl-bundler'

const bundle = bundler([
  (source: string) => `#define PI 3.141592653589793\n${source}`
])

bundle(`
  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }
`)

// #define PI 3.141592653589793
// float rad2deg(float angle) {
//   return angle / PI * 180.0;
// }

Note that the resulting bundle function is itself a transform function

Alternatively, the bundle shortcut can be used to get the same result in a single call:

import { bundle } from '@plutotcool/glsl-bundler'

bundle(`
  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }
`, [
  (source: string) => `#define PI 3.141592653589793\n${source}`
])

Loader

The loader factory creates an asynchronous transform function that resolves #include directives from file system (node) or network (browser):

// ./rad2deg.glsl

#include ./pi.glsl

float rad2deg(float angle) {
  return angle / PI * 180.0;
}
// ./pi.glsl

#define PI 3.141592653589793
import { loader } from '@plutotcool/glsl-bundler'

const load = loader(import.meta.url)

await load(`
  #include ./pi.glsl
  #include ./rad2deg.glsl

  void main() {
    float turn = rad2deg(PI * 2.0);
  }
`)

// #define PI 3.141592653589793
//
// float rad2deg(float angle) {
//   return angle / PI * 180.0;
// }
//
// void main() {
//   float turn = rad2deg(PI * 2.0);
// }

Note that, even if pi.glsl is included twice, it is only outputed once as soon as needed. On node, the loader follows node module resolution using import-meta-resolve.

Alternatively, the load shortcut can be used to load shaders directly from file system or network:

// ./fragment.glsl

#include ./pi.glsl
#include ./rad2deg.glsl

void main() {
  float turn = rad2deg(PI * 2.0);
}
import { load } from '@plutotcool/glsl-bundler'

await load('./fragment.glsl', import.meta.url)

Writer

The writer namespace exposes useful synchronous transform factories for editing glsl code:

import { writer, bundler } from '@plutotcool/glsl-bundler'

const write = bundler([
  // insert line at given index or pattern, negative index starts from the end
  writer.insert('// comment at line 3', 3)
  writer.insert('// comment at last line', -1)
  writer.insert('// comment before uniforms', /\buniform\b/)

   // remove line at given number or pattern
  writer.remove(/\/\/\s+comment line to remove/)

  // insert or override define directive
  // by default to the top of the code, or at given line index or patten
  writer.define('PI', 3.14)
  writer.define('A', true)
  writer.define('B', 'foo', /uniform float a;/)

  // replace given string or pattern
  writer.replace(/\bFragColor\b/g, 'Color')

  // insert or override glsl version, pass null to remove the version directive
  writer.version('300 es')
])

write(`
  #define PI 3.141592653589793

  // comment line to remove

  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }

  uniform float a;

  out vec4 FragColor;

  void main() {
    float turn = rad2deg(PI * 2.0);
    FragColor = vec4(turn / 360.0, 0., 0., 1.);
  }
`)

// #version es 300
// #define A true
// #define PI 3.14
//
// // comment at line 3
//
// float rad2deg(float angle) {
//   return angle / PI * 180.0;
// }
//
// // comment before uniforms
// #define B foo
// uniform float a;
//
// out vec4 Color;
//
// void main() {
//   float turn = rad2deg(PI * 2.0);
//   Color = vec4(turn / 360.0, 0., 0., 1.);
// }
// // comment at last line

Alternatively, the writer modules exposes shortcuts to edit glsl code in a single call:

import {
  insert,
  remove,
  define,
  replace,
  version
} from '@plutotcool/glsl-bundler'

const source = `
  #define PI 3.141592653589793

  // comment line to remove

  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }

  uniform float a;

  out vec4 FragColor;

  void main() {
    float turn = rad2deg(PI * 2.0);
    FragColor = vec4(turn / 360.0, 0., 0., 1.);
  }
`

source = insert(source, '// comment at last line', -1)
source = remove(source, /\/\/\s+comment line to remove/)
source = define(source, 'PI', 3.14)
source = replace(source, /\bFragColor\b/g, 'Color')
source = version(source, '300 es')

// #version es 300
// #define A true
// #define PI 3.14
//
// float rad2deg(float angle) {
//   return angle / PI * 180.0;
// }
//
// uniform float a;
//
// out vec4 Color;
//
// void main() {
//   float turn = rad2deg(PI * 2.0);
//   Color = vec4(turn / 360.0, 0., 0., 1.);
// }
// // comment at last line

Transpiler

The transpiler factory creates a synchronous transform function that transpiles glsl code from and to versions 300 es and 100 es:

import { transpiler } from '@plutotcool/glsl-bundler'

const transpile = transpiler({
  // Default transpile parameters:
  target: 'auto',
  version: 'auto',
  defineTarget: true,
  defineVersion: true
})

transpile(`#version es 300
  in vec3 position;
  in vec2 uv;

  out vec2 vUv;

  void main() {
    vUv = uv;
    gl_Position = vec4(position, 1.);
  }
`)

// #define WEBGL_VERSION 1
// #define GLSL_VERSION 100
// attribute vec3 position;
// attribute vec2 uv;
//
// varying vec2 vUv;
//
// void main() {
//   vUv = uv;
//   gl_Position = vec4(position, 1.);
// }

transpile(`#version es 300
  uniform diffuse sampler2D;

  in vec2 vUv;

  out vec4 FragColor;

  void main() {
    FragColor = texture(diffuse, vUv);
  }
`)

// #define WEBGL_VERSION 1
// #define GLSL_VERSION 100
// uniform diffuse sampler2D;
//
// varying vec2 vUv;
//
// void main() {
//   gl_FragColor = texture2D(diffuse, vUv);
// }

If target is set to 'webgl2' and version is set to 'auto', the transpiler will not transform the syntax since both glsl 100 es and 300 es are supported.

If target is set to 'webgl1' and version is set to 'auto', the transpiler will transform the syntax to support 100 es.

Even if the glsl code have mixed syntaxes (when including dependencies for instance), the transpiler will always output valid glsl; With the syntax that satisfies the version detected from the #version directive, but primarily with a syntax that is supported by the webgl target.

If version is set to '100 es' or '300 es', the transpiler will always transform the syntax to support the given glsl version.

Minifier

The minifier factory creates a synchronous transform function that removes unnecessary characters and renames tokens:

import { minifier } from '@plutotcool/glsl-bundler'

const minify = minifier({
  // Default minify parameters:
  renameFunctions: true,
  renameVariables: true,
  renameDefines: true,
  renameStructs: true,
  trimComments: true,
  trimSpaces: true,
  trimZeros: true
})

minify(`
  #define PI 3.141592653589793

  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }

  void main() {
    float turn = rad2deg(PI * 2.0);
  }
`)

// #define a 3.141592653589793
// float b(float c){return c/a*180.;}void main(){float d=b(a*2.);}

Alternatively, the minify shortcut can be used to get the same result in a single call:

import { minify } from '@plutotcool/glsl-bundler'

minify(`
  #define PI 3.141592653589793

  float rad2deg(float angle) {
    return angle / PI * 180.0;
  }

  void main() {
    float turn = rad2deg(PI * 2.0);
  }
`, { /* Minify parameters */ }, [ /* Additional transform functions */ ])