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

wgsl-preprocessor

v1.0.1

Published

A simple, template literals-based preprocessor for WGSL shaders

Downloads

594

Readme

WGSL Preprocessor

This library provides a dirt-simple way of adding simple preprocessor macros to your WGSL shaders via tagged templates. It supports:

  • #if
  • #elif
  • #else
  • #endif

Most other typical preprocessor functionality can be supported via JavaScript's template literals.

Installation

You can install from npm:

npm install wgsl-preprocessor

And then import into your code as a standard ES6 module:

import { wgsl } from './node-modules/wgsl-preprocessor/wgsl-preprocessor.js';

Or use without an install step by loading it from a CDN like jsdelivr:

import { wgsl } from 'https://cdn.jsdelivr.net/npm/[email protected]/wgsl-preprocessor.js';

Or, you know, just copy and paste the contents of wgsl-preprocessor.js where ever is convenient for you. It's less than a 100 lines of code, we really don't need to overthink it!

Usage

Import the wgsl symbol from wherever you've installed wgsl-preprocessor.js and use it as a tag for a template literal string:

import { wgsl } from 'https://cdn.jsdelivr.net/npm/[email protected]/wgsl-preprocessor.js';

function getDebugShader(sRGB = false) {
  return wgsl`
  @stage(fragment)
  fn main() -> @location(0) vec4<f32> {
    let color = vec4(1.0, 0.0, 0.0, 1.0);
  #if ${sRGB}
    let rgb = pow(color.rgb, vec3(1.0 / 2.2));
    return vec4(rgb, color.a);
  #else
    return color;
  #endif
  }`;
}
`

When using #if or #elif the preprocessor symbol must be followed by a substitution expression that will be evaluated as a boolean.

wgsl`
  #if ${someVar}         // Valid
  #endif

  #if ${x > 5}           // Valid
  #endif

  #if ${someFunction()}  // Valid
  #endif

  #if true               // Invalid
  #endif

  #if 1                  // Invalid
  #endif
`;

If the result of the expression is truthy then the string contents between the opening and closing tags will be returned as part of the string, otherwise it will be omitted.

const x = 1;
const source = wgsl`
#if ${x < 3}
  let a = 1;
#endif

#if ${x > 3}
  let b = 2;
#endif

#if ${x == 3}
  let c = 3;
#else
  let c = 0;
#endif

#if ${x == 4}
  let d = 4;
#elif ${x == 1}
  let d = 1;
#else
  let d = 0;
#endif
`;

// source will be:
// let a = 1
//
//
// let c = 0;
//
// let d = 1;

#if/#elif statements may be nested any number of levels deep:

wgsl`
#if ${shadowsEnabled}
  #if ${lightType == 'point'}
    let shadowAmount = computePointLightShadow();
  #elif ${lightType == 'spot'}
    let shadowAmount = computeSpotLightShadow();
  #else
    let shadowAmount = computeDirectionalShadow();
  #endif
  lightFactor = lightFactor - shadowAmount;
#endif
`;

And any number of #elifs may be chained:

wgsl`
#if ${sampleCount == 1}
  var sampleOffsets : array<vec2<f32>, 1> = array<vec2<f32>, 1>(
    vec2(0.0, 0.0)
  );
#elif ${sampleCount == 2}
  var sampleOffsets : array<vec2<f32>, 2> = array<vec2<f32>, 2>(
    vec2(-0.5, -0.5), vec2(0.5, 0.5)
  );
#elif ${sampleCount == 4}
  var sampleOffsets : array<vec2<f32>, 4> = array<vec2<f32>, 4>(
    vec2(-0.5, -0.5), vec2(-0.5, 0.5), vec2(0.5, -0.5), vec2(0.5, 0.5),
  );
#elif ${sampleCount == 8}
  // Etc...
#endif
`;

Why no #define?

If you need something to approximate a #define statement from other preprocessors, simply use JavaScript's built-in substitution expressions! You don't even need the wgsl tag!

const ambientFactor = 1.0;
const sampleCount = 2;

const source = `
  let ambientFactor = f32(${ambientFactor});

  for (var i = 0u; i < ${sampleCount}u; i = i + 1u) {
    // Etc...
  }
`;

But of course, they play nicely with the wgsl tag too:

const useApproximateSRGB = true;
const approxGamma = 2.2;

export const colorConversions = wgsl`
  fn linearTosRGB(linear : vec3<f32>) -> vec3<f32> {
    #if ${useApproximateSRGB}
      let INV_GAMMA = 1.0 / ${approxGamma};
      return pow(linear, vec3(INV_GAMMA));
    #else
      if (all(linear <= vec3(0.0031308))) {
        return linear * 12.92;
      }
      return (pow(abs(linear), vec3(1.0/2.4)) * 1.055) - vec3(0.055);
    #endif
  }
`;