GLSL Anti-aliased Step Function

raw JSON →
1.0.1 verified Sat Apr 25 auth: no javascript

glsl-aastep is a GLSL utility function designed to provide anti-aliased edges for smoothstep-like operations within shaders. It leverages the `GL_OES_standard_derivatives` extension to calculate fragment derivatives, enabling resolution-independent smoothing of transitions across a single pixel width, regardless of magnification. If the extension is unavailable, it gracefully degrades to a standard `step()` function, losing the anti-aliasing effect. The package is currently at version 1.0.1 and appears to be stable, with updates released infrequently as it provides a focused, atomic GLSL function. Its primary differentiation lies in its direct application of standard derivatives for efficient and robust edge smoothing in 2D and 3D rendering contexts, avoiding more complex multi-pass techniques.

error Shader compilation error: undeclared identifier 'dFdx'
cause Attempting to use `glsl-aastep` (which internally uses `dFdx` and `dFdy`) without explicitly enabling the `GL_OES_standard_derivatives` extension in your GLSL shader.
fix
Add #ifdef GL_OES_standard_derivatives\n#extension GL_OES_standard_derivatives : enable\n#endif to the preamble of your GLSL shader, before any usage of aastep.
error Cannot find module 'glsl-aastep'
cause This error can occur in a JavaScript/TypeScript build process if `glslify` is not correctly configured to process your GLSL shader files, or if you are attempting to `import` or `require` `glsl-aastep` directly as a JS/TS module.
fix
Ensure glsl-aastep is installed as an npm dependency (npm install glsl-aastep --save-dev). Verify that your build pipeline (e.g., Webpack, Rollup) includes glslify as a preprocessor or loader for your .glsl files, and that your GLSL source correctly uses #pragma glslify: aastep = require('glsl-aastep').
gotcha The anti-aliasing functionality of `glsl-aastep` relies on the `GL_OES_standard_derivatives` GLSL extension. If this extension is not enabled in your shader, the function will fall back to a standard `step()` operation, resulting in aliased edges without any smoothing.
fix Ensure your GLSL shader includes `#ifdef GL_OES_standard_derivatives #extension GL_OES_standard_derivatives : enable #endif` at the top, typically after the `precision` declaration, to enable the necessary derivative calculations.
npm install glsl-aastep
yarn add glsl-aastep
pnpm add glsl-aastep

Demonstrates how to use `glslify` in a JavaScript environment to bundle `glsl-aastep` into a final GLSL shader string, then compile and render it within a basic WebGL context to show an anti-aliased circle.

import { readFileSync } from 'fs';
import { createRequire } from 'module';
import path from 'path';
import { fileURLToPath } from 'url';

// For ESM context: create a CommonJS-like require for glslify
const require = createRequire(import.meta.url);
const glslify = require('glslify');
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// This GLSL template uses glsl-aastep
const shaderSourceTemplate = `
precision highp float;

#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif

// glslify automatically resolves 'glsl-aastep' from node_modules
#pragma glslify: aastep = require('glsl-aastep')

uniform float iGlobalTime;
uniform vec2  iResolution;

void main() {
  vec2 uv = vec2(gl_FragCoord.xy / iResolution.xy) - 0.5;
  uv.x *= iResolution.x / iResolution.y;

  // Simple animation for demonstration
  float zoom = 1.0 + sin(iGlobalTime * 0.5) * 0.5;
  uv /= zoom;

  float len = length(uv);

  // Apply anti-aliased step function
  float color = aastep(0.5, len);

  gl_FragColor.rgb = vec3(color);
  gl_FragColor.a   = 1.0;
}
`;

// Use glslify to bundle the shader source
const finalShaderSource = glslify.compile(shaderSourceTemplate);

console.log('--- Compiled Shader Source ---');
console.log(finalShaderSource);
console.log('------------------------------');

// Below is conceptual WebGL usage for a browser environment
// This part would typically run in a browser after the glslify bundling step
if (typeof document !== 'undefined') {
  const canvas = document.createElement('canvas');
  document.body.appendChild(canvas);
  const gl = canvas.getContext('webgl');

  if (!gl) {
    console.error('WebGL not supported');
  } else {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    const program = gl.createProgram();
    const vsSource = `attribute vec4 a_position; void main() { gl_Position = a_position; }`;
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vsSource);
    gl.compileShader(vertexShader);
    gl.attachShader(program, vertexShader);

    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, finalShaderSource);
    gl.compileShader(fragmentShader);
    gl.attachShader(program, fragmentShader);

    gl.linkProgram(program);
    gl.useProgram(program);

    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW);

    const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
    gl.enableVertexAttribArray(positionAttributeLocation);
    gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

    const iResolutionUniformLocation = gl.getUniformLocation(program, 'iResolution');
    const iGlobalTimeUniformLocation = gl.getUniformLocation(program, 'iGlobalTime');

    function render(time) {
      gl.uniform2f(iResolutionUniformLocation, gl.canvas.width, gl.canvas.height);
      gl.uniform1f(iGlobalTimeUniformLocation, time * 0.001);
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.drawArrays(gl.TRIANGLES, 0, 6);
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
  }
}