npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vite-plugin-glslify-inject

v1.1.2

Published

A plugin to write GLSL more comfortably.

Downloads

547

Readme

vite-plugin-glslify-inject

Why | How to use | Output | How to set up

  • Write GLSL in distinct files with the glslify module system;
  • Import them as ES6 modules;
  • Inject contants at runtime;
  • Auto-generate TS types for your modules, constants and uniforms;
  • Compatible with linters such as GLSL Lint.

Why

Organising code in distinct files and dedicated file types is a matter of preference.

This plugin enables a setup where you keep GLSL seperate from your JS while having all the advantages of glslify module resolution and runtime code injection, without having to deal with string interpolation, potentially loosing GLSL linting in the process, and in what I consider a cleaner format.

Getting TS types for your uniforms at the TS/GLSL interface is also nice to have. You probably already do it for other APIs.

How to use

In this example we create a material that takes a grayscales texture and styles it with false colors.

main.ts

import passThrough from '@shaders/passThrough.vert';
import { falseColorsWith, FalseColorsUniforms } from '@shaders/falseColors.frag';

const gradient = ["#00178f", "#006172", "#47dd00"] as const;
const steps = gradient.length;

const material = new THREE.RawShaderMaterial({
    uniforms: parseUniforms<FalseColorsUniforms>()({ gradient, myTexture }),
    vertexShader: passThrough,
    fragmentShader: falseColorsWith({ steps })
});

I don't detail parseUniforms but even with a library like threejs you probably do some processing on your uniforms before passing them to your material.

flaseColors.frag

We import the interColor util with a glslify pragma.

We define uniform vec3 gradient[steps] where steps can be overwritten at runtime.

#pragma glslify: interColor = require(./lib/interColor.glsl)

precision highp float;
precision highp int;

varying vec2 vUv;

const int steps = 2; // overwritten at runtime

uniform vec3 gradient[steps];
uniform sampler2D myTexture;

void main() {
    float alpha = texture2D(myTexture, vUv).r;

    vec3 rgb = interColor(gradient, alpha);

    gl_FragColor = vec4(rgb, 1);
}

lib/interColor.glsl

#pragma glslify: export(interColor)

precision highp float;

vec3 interColor (vec3 gradient[2], float t) {
    return mix(gradient[0], gradient[1], t);
}

vec3 interColor (vec3 gradient[3], float t) {
    vec3 color;
    if(t <= 0.5) {
        color = mix(gradient[0], gradient[1], t * 2.0);
    }  else {
        color = mix(gradient[1], gradient[2], (t - 0.5) * 2.0);
    }
    return color;
}

vec3 interColor (vec3 gradient[4], float t) {

// [...]

Output

Runtime values

At current, precision specifiers are not deduplicated and there is no compile-time check to see if they are consistent.

precision highp float;

vec3 interColor (vec3 gradient[2], float t) {
    return mix(gradient[0], gradient[1], t);
}

vec3 interColor (vec3 gradient[3], float t) {
    vec3 color;
    if(t <= 0.5) {
        color = mix(gradient[0], gradient[1], t * 2.0);
    }  else {
        color = mix(gradient[1], gradient[2], (t - 0.5) * 2.0);
    }
    return color;
}

vec3 interColor (vec3 gradient[4], float t) {

// [...]

precision highp float;
precision highp int;
#define GLSLIFY 1

varying vec2 vUv;

const int steps = 2; // overwritten at runtime

uniform vec3 gradient[steps];
uniform sampler2D myTexture;

void main() {
    float alpha = texture2D(myTexture, vUv).r;

    vec3 rgb = interColor(gradient, alpha);

    gl_FragColor = vec4(rgb, 1);
}

Types

The following types are auto generated next to their source and updated live while the dev server is running. A variety of exports are available to enable different import patterns.

declare module '@shaders/passThrough.vert' {
    const passThrough: string;

    export { passThrough as default, passThrough as glsl, passThrough };
}
declare module '#shaders/falseColors.frag' {
    namespace THREE {
        export type Vector3 = { x: number, y: number, z: number, isVector3: true };
        export type Color = { r: number, g: number, b: number, isColor: true };
        export type Texture = { image: unknown, isTexture: true, isCubeTexture?: never };
    }

    const falseColors: string;

    const inject: (map: { steps?: number }) => string;

    type Uniforms = {
        gradient: Array<number> | Float32Array | Array<[number, number, number]> | Array<THREE.Vector3> | Array<THREE.Color>,
        myTexture: WebGLTexture | THREE.Texture
    };

    export {
        falseColors as default,
        falseColors as glsl,
        falseColors,
        inject,
        inject as falseColorsWith,
        Uniforms,
        Uniforms as FalseColorsUniforms
    };
}

Because gradient is an array which length is defined by a constant, and because constants can be injected at runtime, Uniform.gradient lists array types instead of tuples.

See How to set up to control this output.

How to set up

npm install --save-dev vite-plugin-glslify-inject

The vite.config.js corresponding to the above example:

  • we invoke the plugin;
  • we pass in your include/exclude patterns;
  • we pass a path alias pointing to the location of your shaders for .d.ts modules to be generated;
  • we pass a library preset to generate threejs types (in addition to native types) for uniforms.
import { defineConfig } from 'vite'
import glsl from 'vite-plugin-glslify-inject'

export default defineConfig({
    plugins: [
        glsl({
            include: './src/shaders/**/*.(vert|frag)',
            exclude: 'node_modules/**',
            types: { alias: '@shaders', library: 'threejs' }
        })
    ],
    resolve: {
        alias: {
          '@shaders': "/src/shaders/",
        }
    }
})

Uniforms

You can disable the generation of type definitions for uniforms if you don't need them. There is no need to specify a library if you do so:

glsl({
    include: './src/shaders/**/*.(vert|frag)',
    exclude: 'node_modules/**',
    types: { alias: '@shaders', uniforms: false }
})

Libraries

You are likely using a library which bundles your uniforms for you and enables higher level data structures than flat/typed arrays, hence the library field.

In the example above we pass in the string threejs. I may add types for other libraries, but you can also inject your own types using the types fields in the configuration.

Array types

The plugin can handle the nesting of types in arrays for you, but your library definition needs to opt-in for it. It is the case of the built-in threejs for example.

Here is a minimal example for the trivial shader uniform vec2 foo[2]:

// config
library: { nesting: true }
// output
type Uniforms = {
    foo: [number, number, number, number] | Float32Array |  [[number, number],  [number, number]]
};

Base types

The library field takes a types field which is a record of type definition lists. Each GLSL type accepts an array of TS types (they only need to be specific enough to be safe. Don't copy and paste the whole definitions).

Here are a few ways you could add a vec2 type definition for the trivial shader uniform vec2 foo;. The choice of variant only affects tooltips.

Inline
// config
library: {
    types: {
        vec2: ['{ x: number, y: number, isVector2: true }']
    }
}
// output
type Uniforms = {
    foo: [number, number] | Float32Array | { x: number, y: number, isVector2: true }
};
Alias
// config
library: {
    types: {
        vec2: [{
            alias: 'Vector2',
            type: '{ x: number, y: number, isVector2: true }'
        }]
    }
}
// output
type Vector2 = { x: number, y: number, isVector2: true };
type Uniforms = {
    foo: [number, number] | Float32Array | Vector2
};
Namespaced alias
// config
library: {
    namespace: 'THREE'
    types: {
        vec2: [{
            alias: 'Vector2',
            type: '{ x: number, y: number, isVector2: true }'
        }]
    }
}
// output
namespace THREE {
    export type Vector2 = { x: number, y: number, isVector2: true };
}
type Uniforms = {
    foo: [number, number] | Float32Array | THREE.Vector2
};