GLSL Anti-aliased Step Function
raw JSON →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.
Common errors
error Shader compilation error: undeclared identifier 'dFdx' ↓
#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' ↓
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'). Warnings
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. ↓
Install
npm install glsl-aastep yarn add glsl-aastep pnpm add glsl-aastep Imports
- aastep wrong
import { aastep } from 'glsl-aastep'; // Incorrect for GLSL modules const aastep = require('glsl-aastep'); // Incorrect for GLSL modules directly in JS/TScorrect#pragma glslify: aastep = require('glsl-aastep')
Quickstart
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);
}
}