Files
AR-Menu/.agents/skills/needle-engine/references/postprocessing.md
pelpanagiotis a7c53a08a0 Initial commit: Unity Needle AR Menu project with MenuScene and SampleScene web apps
Add root .gitignore for Unity Library/Temp/Logs, IDE folders, and node_modules.
Include Assets, Needle TypeScript (MenuController, asset picker, WebXR), and project configuration.

Made-with: Cursor
2026-04-19 22:41:05 +03:00

4.2 KiB

Needle Engine — Post-Processing Reference

Needle Engine uses the pmndrs postprocessing library. Postprocessing loads asynchronously via NEEDLE_ENGINE_MODULES.POSTPROCESSING (same pattern as physics). Add and remove effects via this.context.postprocessing.

API (this.context.postprocessing)

import { BloomEffect } from "@needle-tools/engine";

// Add/remove effects
const bloom = new BloomEffect();
bloom.intensity.value = 3;
bloom.threshold.value = 0.5;
this.context.postprocessing.addEffect(bloom);
this.context.postprocessing.removeEffect(bloom);

// Other API
this.context.postprocessing.markDirty();             // force rebuild next frame
this.context.postprocessing.effects;                 // readonly array of active effects
this.context.postprocessing.multisampling = "auto";  // "auto" or number (0 to max)
this.context.postprocessing.adaptiveResolution = true; // reduce DPR when FPS drops

Built-in effects

All imported from @needle-tools/engine. Properties use VolumeParameter — set values with .value:

// Bloom — glow on bright areas
const bloom = new BloomEffect();
bloom.threshold.value = 0.9;     // brightness cutoff (default: 0.9)
bloom.intensity.value = 1;       // glow strength (default: 1)
bloom.scatter.value = 0.7;       // spread (default: 0.7)

// Depth of Field — focus blur
import { DepthOfField, DepthOfFieldMode } from "@needle-tools/engine";
const dof = new DepthOfField();
dof.mode = DepthOfFieldMode.Bokeh;  // Off, Gaussian, or Bokeh
dof.focusDistance.value = 1;         // focus distance
dof.focalLength.value = 0.2;        // focus range
dof.aperture.value = 20;            // bokeh scale

// Vignette — darkened edges
const vig = new Vignette();
vig.intensity.value = 0.5;      // darkness (default: 0)
vig.color.value = { r: 0, g: 0, b: 0, a: 1 };

// Color Adjustments — exposure, contrast, hue, saturation
const ca = new ColorAdjustments();
ca.postExposure.value = 1;      // exposure (default: 1)
ca.contrast.value = 0;          // -1 to 1
ca.hueShift.value = 0;          // hue rotation
ca.saturation.value = 0;        // saturation adjustment

// Tonemapping
const tm = new ToneMappingEffect();
tm.setMode("AgX");              // ACES, AgX, Neutral, etc.
tm.exposure.value = 1;

// Chromatic Aberration — color fringing
const chr = new ChromaticAberration();
chr.intensity.value = 0.5;

// Pixelation
const pix = new PixelationEffect();
pix.granularity.value = 10;     // pixel size

// SSAO — ambient occlusion
const ssao = new ScreenSpaceAmbientOcclusion();
ssao.intensity.value = 2;
ssao.samples.value = 9;         // quality vs performance
ssao.falloff.value = 1;
ssao.color.value = new Color(0, 0, 0);

// N8AO — alternative AO (higher quality)
import { ScreenSpaceAmbientOcclusionN8, ScreenSpaceAmbientOcclusionN8QualityMode } from "@needle-tools/engine";
const n8ao = new ScreenSpaceAmbientOcclusionN8();
n8ao.aoRadius.value = 1;        // world-space radius
n8ao.intensity.value = 1;
n8ao.quality = ScreenSpaceAmbientOcclusionN8QualityMode.Medium;

// Antialiasing (SMAA)
const aa = new Antialiasing();
aa.preset.value = 2;            // 0=Low, 1=Medium, 2=High, 3=Ultra

// Tilt Shift — miniature/diorama look
const ts = new TiltShiftEffect();
ts.focusArea.value = 0.4;       // in-focus band size
ts.feather.value = 0.3;         // blur transition
ts.offset.value = 0;            // vertical offset
ts.rotation.value = 0;          // angle

// Sharpening
const sharp = new SharpeningEffect();
sharp.amount = 1;               // strength (direct property, not VolumeParameter)
sharp.radius = 1;               // radius

Runtime parameter changes

// VolumeParameter values update the underlying shader uniforms immediately
bloom.intensity.value = 5;  // takes effect next frame, no rebuild needed

// Enable/disable individual effects
bloom.enabled = false;       // removes from pipeline

Notes

  • Post-processing is disabled during XR sessions.
  • Multisampling auto-adjusts: disabled when SMAA is present, scales down on low FPS, scales up when stable.
  • Effects are automatically ordered (Bloom before Vignette before ToneMapping, etc.). Custom effects can set order to control placement.
  • Alpha is preserved through the pipeline.