| name | xor-shader-techniques |
| description | Use when implementing shader effects like turbulence, fluid, fire, smoke,
procedural noise, starfields, volumetric rendering, raymarching, glow,
antialiasing, fractal texturing, or optimizing shader performance with cheap alternatives.
Triggers on: shader techniques, shader tricks, shader optimization,
turbulence shader, fluid shader, fire shader, smoke shader,
procedural noise, dot noise, gyroid noise,
efficient chaos, star field, particle scatter,
fractal texturing, LOD texturing, texture scaling,
volumetric rendering, raymarching, glow effects,
antialiasing, analytic antialiasing, fwidth,
shader performance, cheap shader effects,
GLSL tricks, shader math, shader formulas,
Xor shader, GM Shaders, mini.gmshaders.com
|
Xor Shader Techniques
Advanced shader programming techniques by Xor (@XorDev), covering efficient methods for procedural generation, noise, texturing, and visual effects.
Triggers
- shader techniques, shader tricks, shader optimization
- turbulence shader, fluid shader, fire shader, smoke shader
- procedural noise, dot noise, gyroid noise
- efficient chaos, star field, particle scatter
- fractal texturing, LOD texturing, texture scaling
- volumetric rendering, raymarching, glow effects
- antialiasing, analytic antialiasing, fwidth
- shader performance, cheap shader effects
- GLSL tricks, shader math, shader formulas
- Xor shader, GM Shaders, mini.gmshaders.com
Overview
This skill provides comprehensive guidance on advanced shader programming techniques discovered and refined by Xor through 14 years of shader development. These techniques emphasize performance, simplicity, and visual quality, offering cheap alternatives to expensive operations while maintaining impressive results.
Core Philosophy:
- Simple formulas for complex effects
- Performance-first approach
- Avoid expensive operations when possible
- Fake it well rather than simulate it perfectly
- Leverage mathematical properties (golden ratio, irrational numbers, rotation)
Technique Catalog
1. Turbulence (Fluid Approximation)
Use Cases: Water, fire, dust, magic, wind, fog, smoke, clouds
Core Concept:
Approximate fluid dynamics using layered sine waves instead of solving Navier-Stokes equations. Each wave is rotated and scaled to break alignment patterns.
Basic Formula:
// Number of turbulence waves
#define TURB_NUM 10.0
// Turbulence wave amplitude
#define TURB_AMP 0.7
// Turbulence wave speed
#define TURB_SPEED 0.3
// Turbulence frequency
#define TURB_FREQ 2.0
// Turbulence frequency multiplier
#define TURB_EXP 1.4
// Turbulence starting scale
float freq = TURB_FREQ;
// Turbulence rotation matrix (arbitrary angle, not 45° or 90°)
mat2 rot = mat2(0.6, -0.8, 0.8, 0.6);
// Loop through turbulence octaves
for(float i=0.0; i<TURB_NUM; i++)
{
// Scroll along the rotated y coordinate
float phase = freq * (pos * rot).y + TURB_SPEED*iTime + i;
// Add a perpendicular sine wave offset
pos += TURB_AMP * rot[0] * sin(phase) / freq;
// Rotate for the next octave
rot *= mat2(0.6, -0.8, 0.8, 0.6);
// Scale down for the next octave
freq *= TURB_EXP;
}
Key Parameters:
TURB_NUM: Number of waves (8-10 for good results)
TURB_AMP: Overall waviness (0.0-1.0)
TURB_FREQ: Starting frequency (2.0-4.0)
TURB_EXP: Frequency multiplier (1.3-1.6 for smooth, higher for detailed)
Fire Variation:
- Compress horizontally and stretch vertically initially
- Scroll upward
- Expand horizontally as it rises
- Creates upward expansion effect
ShaderToy Demo: https://shadertoy.com/view/wltcRS
2. Efficient Chaos (Fast Pseudo-Random Scattering)
Use Cases: Stars, rain, leaves, particles, object scattering
Problem Solved:
Traditional Worley noise requires sampling all neighboring cells (3^N complexity: 9 in 2D, 27 in 3D). This method works in any dimension with constant cost.
Core Concept:
Layer multiple grids with rotation and scaling. No hash functions or neighbor sampling needed.
Basic Formula:
// Number of layers
#define LAYERS 5.0
// Overall scale
#define SCALE 0.1
// Overall brightness
#define BRIGHTNESS 0.04
// Layer shift (shifting factor)
#define LAYER_SHIFT 2.618
// Layer spacing (spacing factor)
#define LAYER_SCALE 0.6
// Output color
vec3 col = vec3(0.0);
// Starfield coordinates
vec2 c = fragCoord / iResolution.y / SCALE;
// Loop through layers
for(float i = 0.5 / LAYERS; i<1.0; i+=1.0 / LAYERS)
{
// Rotate layer (Golden Angle: ~137.5°)
c *= mat2(0.22252093, -0.97492791, 0.97492791, 0.22252093);
// Get rotated coordinates
vec2 p = c;
// Apply layer shifting
p += LAYER_SHIFT*i;
// Adjust layer scaling
p /= 1.0 + LAYER_SCALE*i;
// Distance to cell center
float len = length(mod(p, 2.0) - 1.0);
// Light attenuation with circle cutoff
float att = max(1.0-len, 0.0) / len;
col += att;
}
Breaking Patterns:
// Add waves to break visible axis lines (for 2 layers)
#define LAYER_WAVES 0.2
p += LAYER_WAVES*sin(p.yx);
3D Version:
Works identically in 3D without extra cost! Just use vec3 and mat3 for golden angle rotation.
Key Advantages:
- O(N) complexity instead of O(3^N)
- Works in any dimension
- No hash/rand functions needed
- No neighbor cell sampling
ShaderToy Demo: https://shadertoy.com/view/wfGyzR
3. Dot Noise (Cheap 3D Noise)
Use Cases: Clouds, water, terrain, procedural texturing, volumetric rendering
Problem Solved:
3D Value/Perlin/Simplex noise are expensive when sampled many times per pixel. This provides a fast alternative.
Core Concept:
Aperiodic gyroids using golden ratio and golden angle rotation. No hash, interpolation, or multi-sampling needed.
Formula:
float dot_noise(vec3 p)
{
// The golden ratio
const float PHI = 1.618033988;
// Rotating the golden angle on the vec3(1, phi, phi*phi) axis
const mat3 GOLD = mat3(
-0.571464913, +0.814921382, +0.096597072,
-0.278044873, -0.303026659, +0.911518454,
+0.772087367, +0.494042493, +0.399753815);
// Gyroid with irrational orientations and scales
return dot(cos(GOLD * p), sin(PHI * p * GOLD));
// Ranges from [-3 to +3]
}
Mathematical Background:
- Gyroids:
dot(cos(p), sin(p.yzx)) creates wavy infinite shapes
- Golden ratio (φ): Most irrational number, prevents period alignment
- Golden angle rotation: Most irrational orientation
- Result: Aperiodic pseudo-noise without traditional noise functions
Best Use Cases:
- Low-scale noise on natural shapes
- Clouds and fluids
- Fractal noise layers (fast accumulation)
- Volumetric rendering (many samples per pixel)
Limitations:
- Visible patterns in flat, large-scale scenes
- Still composed of periodic waves (just rotated)
- Less suitable for terrain at large scales
Fractal Layering:
float fractal_dot_noise(vec3 p)
{
float result = 0.0;
float amp = 1.0;
float freq = 1.0;
for(int i = 0; i < 4; i++)
{
result += amp * dot_noise(p * freq);
amp *= 0.5;
freq *= 2.0;
}
return result;
}
ShaderToy Demo: https://shadertoy.com/view/DfGGzm
4. Fractal Texturing (Scale-Consistent LOD)
Use Cases: Terrain rendering, large-scale games, natural textures
Problem Solved:
- Distance textures repeat too much (artificial)
- Close textures lose detail (blur)
- Traditional mipmapping doesn't solve repetition
Core Concept:
Scale textures inversely with depth, round to discrete LOD levels, and blend between levels.
Formula:
vec4 fractal_texture(sampler2D tex, vec2 uv, float depth)
{
float LOD = log(depth);
float LOD_floor = floor(LOD);
float LOD_fract = LOD - LOD_floor;
vec2 uv1 = uv / exp(LOD_floor - 1.0);
vec2 uv2 = uv / exp(LOD_floor + 0.0);
vec2 uv3 = uv / exp(LOD_floor + 1.0);
vec4 tex0 = texture2D(tex, uv1);
vec4 tex1 = texture2D(tex, uv2);
vec4 tex2 = texture2D(tex, uv3);
return (tex1 + mix(tex0, tex2, LOD_fract)) * 0.5;
}
Scale Calibration:
// Adjust based on: depth units, texture scale, screen resolution
depth *= 1e3 / iResolution.y;
How It Works:
- Take log of depth to get LOD level
- Round down to discrete levels
- Sample at 3 LOD levels (floor-1, floor, floor+1)
- Blend between levels based on fractional part
- Average adjacent levels for smooth transition
Best For:
- Natural textures (grass, terrain, stone)
- Consistent detail at all distances
Not Ideal For:
- Structured textures (bricks, tiles) - edges won't blend naturally
ShaderToy Demo: https://shadertoy.com/view/NdVfzw
5. fwidth Outlines
Use Cases: Quick outlines, edge detection, cel shading
Core Concept:
Use derivative functions to detect edges almost for free. Works with any continuous gradient.
Basic Formula:
// For any distance field or continuous function
float outline = step(fwidth(dist), abs(dist));
// Alternative with thickness control
float outline = smoothstep(0.0, fwidth(dist), abs(dist));
Quality:
Not the best quality, but extremely cheap. Works in a pinch for:
- Debug visualization
- Stylized rendering
- Quick edge detection
Limitations:
- Coarse 2x2 derivative blocks can cause artifacts
- Only works with continuous functions
- Not as precise as dedicated outline methods
6. Volumetric Raymarching (Glow Effects)
Use Cases: Clouds, fire, smoke, light rays, fog, volumetric lighting
Core Concept:
Similar to SDF raymarching, but use density fields instead of distance fields. Accumulate color samples along the ray.
Density Field Example:
// Density field (Tunnel + irregular gyroid)
float volume(vec3 p)
{
return 3.5 - 0.25*length(p.xy) + 0.5*dot(sin(p), cos(p*0.618).yzx);
}
Glow Raymarch Loop:
#define BRIGHTNESS 0.002
#define STEPS 100.0
vec3 col = vec3(0.0);
for(float i = 0.0; i<STEPS; i++)
{
// Glow density
float vol = volume(pos);
// Step forward
pos += dir * vol;
// Add the sample color (inverse square attenuation)
col += vec3(3, 2, 1) / vol;
}
// Tanh tonemapping
col = tanh(BRIGHTNESS*col);
Alpha Blending (for clouds/smoke):
color = mix(color, vec4(sample_rgb, 1), (1.0 - color.a) * sample_alpha);
// Stop when opaque (optimization)
if (color.a > 0.998) break;
Depth of Field with Raymarching:
// Focus ratio should be small like 0.1 or 0.01
float focus = abs(depth - focal_point) * focal_ratio;
// Step through when out of focus
step_dist = max(step_dist - focus, focus);
Key Differences from SDF Raymarching:
- Use density instead of distance
- Accumulate color at every step (not just final intersection)
- Allow passthrough (never reach zero step size)
- Smaller steps in high-density areas
ShaderToy Demos:
7. Analytic Anti-Aliasing
Use Cases: Smooth edges, filtering, preventing aliasing artifacts
Problem: Aliasing occurs when we have more color data than pixels can represent.
Solution Levels:
Level 1: SDF-Based (Cheapest, Best Quality)
For circles, lines, squares, or any signed distance field:
// dist = distance to edge (in world units)
// texel = pixel size in world units
float gradient = clamp((radius - dist) / texel + 0.5, 0.0, 1.0);
Requirements:
- Accurate distance field
- Known pixel scale
Level 2: fwidth-Based (General Purpose)
For any continuous function with inconsistent gradients:
// Approximate smooth edges from any continuous function
float antialias_l1(float d)
{
// Divide d by its derivative width
return clamp(0.5 + d / fwidth(d), 0.0, 1.0);
}
// Better quality version (L2 norm)
float antialias_l2(float d)
{
// x and y derivatives
vec2 dxy = vec2(dFdx(d), dFdy(d));
// Get gradient width
float width = length(dxy);
// Calculate reciprocal scale (avoid division by 0!)
float scale = width > 0.0 ? 1.0/width : 1e7;
// Normalize the gradient d with its scale
return clamp(0.5 + 0.7 * scale * d, 0.0, 1.0);
}
Use For:
- Distorted distance fields
- Noise functions
- Any continuous gradient
Level 3: Manual Derivatives (Edge Cases)
When 2x2 derivative blocks are too coarse or function is discontinuous:
// For when derivatives must be manually calculated
float antialias_l2_dxy(float d, vec2 dxy)
{
float width = length(dxy);
float scale = width > 0.0 ? 1.0/width : 1e7;
return clamp(0.5 + 0.7 * scale * d, 0.0, 1.0);
}
// Compute manual derivatives (3 samples)
float grad00 = grad(pos);
float grad10 = grad(pos + vec2(1, 0));
float grad01 = grad(pos + vec2(0, 1));
vec2 dxy = vec2(grad10, grad01) - grad00;
// Use with antialiasing
float aa = antialias_l2_dxy(grad00, dxy);
Use For:
- Discontinuous functions (floor, fract, step)
- Stripy patterns with many edges
- When fwidth produces artifacts
- Fine detail requiring precision
Handling Discontinuities:
- Use continuous version of gradient for derivative calculation
- Apply discontinuous function after anti-aliasing
- Example:
fract(grad) - 0.5 breaks derivatives → use grad for derivatives, then apply fract