GLSL Shader Loader for Webpack

0.1.6 · maintenance · verified Tue Apr 21

glsl-shader-loader is a Webpack loader designed to bundle GLSL shader source code, enabling modular management of shaders for WebGL applications. It allows developers to organize GLSL functions into separate files and import them using a custom `#pragma loader: import` syntax directly within other `.glsl` files. The loader performs static analysis to resolve dependencies, remove unused functions, and ensure functions are imported only once, resulting in an optimized shader string ready for use with WebGL. As of version 0.1.6, it focuses on providing a preprocessor-like experience for GLSL, which is useful for complex shader graphs and code reuse. Its release cadence appears to be slow, with the latest version indicating an early stage or a stable, low-maintenance tool rather than rapid development. Key differentiators include its syntax tree analysis for dependency resolution and optimization, which goes beyond simple string concatenation.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to configure glsl-shader-loader in Webpack to process `.glsl` files, including internal GLSL `#pragma loader: import` statements, and then import the resulting shader string into a JavaScript application.

import path from 'path';
import webpack from 'webpack';
import MemoryFS from 'memory-fs';

// Basic webpack config to use glsl-shader-loader
const config = {
  mode: 'development',
  entry: './app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.(frag|vert|glsl)$/,
        use: [
          {
            loader: 'glsl-shader-loader',
            options: {
              // Optional: specify a root path for absolute GLSL imports
              // root: path.resolve(__dirname, 'src/shaders')
            }
          }
        ]
      }
    ]
  }
};

// Example application JS (app.js)
const appJsContent = `
import fragmentShaderSource from './fragmentShaderSource.glsl';

console.log('--- Compiled Fragment Shader Source ---');
console.log(fragmentShaderSource);

// In a real WebGL app, you'd use:
// const gl = canvas.getContext('webgl');
// const shader = gl.createShader(gl.FRAGMENT_SHADER);
// gl.shaderSource(shader, fragmentShaderSource);
// gl.compileShader(shader);
`;

// Example GLSL file (fragmentShaderSource.glsl)
const fragmentShaderContent = `
precision mediump float;

varying vec2 v_texCoord;

#pragma loader: import { randomColor } from './utils.glsl';

void main() {
  vec3 color = randomColor(v_texCoord);
  gl_FragColor = vec4(color, 1.0);
}
`;

// Example GLSL utility file (utils.glsl)
const utilsGlslContent = `
vec3 randomColor(vec2 coord) {
  // Simple pseudo-random color based on coordinates
  float r = fract(sin(dot(coord.xy, vec2(12.9898, 78.233))) * 43758.5453);
  float g = fract(sin(dot(coord.xy, vec2(53.123, 19.345))) * 53758.9876);
  float b = fract(sin(dot(coord.xy, vec2(87.654, 34.567))) * 63758.1234);
  return vec3(r, g, b);
}

vec3 anotherFunction() {
  return vec3(0.0);
}
`;

// Setup an in-memory file system for webpack to read from
const fs = new MemoryFS();
fs.mkdirpSync(path.resolve(__dirname, 'dist'));
fs.mkdirpSync(path.resolve(__dirname, 'src'));
fs.mkdirpSync(path.resolve(__dirname, 'utils'));
fs.writeFileSync('./app.js', appJsContent);
fs.writeFileSync('./fragmentShaderSource.glsl', fragmentShaderContent);
fs.writeFileSync('./utils.glsl', utilsGlslContent);

const compiler = webpack(config);
compiler.inputFileSystem = fs;
compiler.outputFileSystem = fs;

compiler.run((err, stats) => {
  if (err) {
    console.error(err.stack || err);
    if (err.details) {
      console.error(err.details);
    }
    return;
  }

  const info = stats.toJson();

  if (stats.hasErrors()) {
    console.error(info.errors);
  }
  if (stats.hasWarnings()) {
    console.warn(info.warnings);
  }

  console.log('\nWebpack build completed.');
  const bundlePath = path.resolve(__dirname, 'dist', 'bundle.js');
  const bundleContent = fs.readFileSync(bundlePath, 'utf8');
  // In a real scenario, you'd typically serve this bundle or inject it.
  // For this quickstart, we'll just show the generated content.
  // To demonstrate the *processed* GLSL, we'd need to run the bundle
  // which is outside the scope of this quickstart directly.
  // The console.log in app.js inside the bundle would show the processed shader.
});

view raw JSON →