Post-Processing Components

PostProcessing and PostProcessVolume components for screen-space effects like bloom, SSAO, DOF, and tone mapping.

Post-processing components enable screen-space visual effects applied after rendering. Web Engine provides a comprehensive suite of effects including bloom, SSAO, depth of field, motion blur, and tone mapping for cinematic visuals.

PostProcessing#

The PostProcessing component defines global post-processing settings that apply to the entire scene. Only one PostProcessing component should exist per scene.

Properties#

PropertyTypeDefaultDescription
enabledui81Enable post-processing pipeline (0=off, 1=on)
bloomIntensityf320.0Bloom effect intensity (0=off, 1.0=strong)
bloomThresholdf321.0Brightness threshold for bloom
bloomSmoothingf320.5Bloom smoothness/radius (0-1)
vignetteDarknessf320.0Vignette darkness (0=off, 1=black edges)
vignetteOffsetf321.0Vignette offset/size (0.5=large, 1.5=small)
chromaticAberrationf320.0Chromatic aberration offset (0=off, 0.01=subtle)
toneMappingExposuref321.0Tone mapping exposure (0.5=dark, 2.0=bright)
ssaoIntensityf320.0SSAO effect intensity (0=off, 1.0=strong)
ssaoRadiusf320.5SSAO sampling radius in world units
ssaoBiasf320.01SSAO bias to prevent self-shadowing artifacts
colorGradingLUTui320Color grading LUT texture asset ID (0=none)
ssrEnabledui80Screen-space reflections (0=off, 1=on)
dofFocusDistancef3210.0Depth of field focus distance (meters)
dofFocalLengthf3250.0Depth of field focal length (mm, 18-200)
dofBokehScalef321.0Depth of field bokeh size
motionBlurIntensityf320.0Motion blur intensity (0=off, 1.0=strong)

Basic Usage#

import { addComponent, PostProcessing } from '@web-engine/core';
// Create global post-processing entity
const postFx = world.addEntity();
addComponent(world, PostProcessing, postFx);
// Enable post-processing
PostProcessing.enabled[postFx] = 1;
// Configure basic effects
PostProcessing.bloomIntensity[postFx] = 0.5;
PostProcessing.bloomThreshold[postFx] = 0.9;
PostProcessing.vignetteDarkness[postFx] = 0.3;
PostProcessing.toneMappingExposure[postFx] = 1.2;

Single PostProcessing Entity

Only one PostProcessing component should exist per scene. Multiple PostProcessing components will cause conflicts. Use PostProcessVolume for localized overrides.

Bloom Effect#

Bloom creates a glow around bright areas, simulating camera lens bloom and light scattering. Perfect for bright lights, magic effects, and sci-fi visuals.

Bloom Properties#

PropertyDescriptionRecommended Range
bloomIntensityOverall bloom strength0.0 - 1.0
bloomThresholdBrightness required to bloom (in HDR)0.8 - 1.2
bloomSmoothingBloom spread/radius0.3 - 0.8

Usage#

// Subtle bloom (realistic)
PostProcessing.bloomIntensity[postFx] = 0.3;
PostProcessing.bloomThreshold[postFx] = 1.0;
PostProcessing.bloomSmoothing[postFx] = 0.5;
// Strong bloom (stylized/magic)
PostProcessing.bloomIntensity[postFx] = 1.0;
PostProcessing.bloomThreshold[postFx] = 0.7;
PostProcessing.bloomSmoothing[postFx] = 0.8;
// Sci-fi glow
PostProcessing.bloomIntensity[postFx] = 0.8;
PostProcessing.bloomThreshold[postFx] = 0.6;
PostProcessing.bloomSmoothing[postFx] = 0.7;
// Disable bloom
PostProcessing.bloomIntensity[postFx] = 0.0;

Bloom Threshold

Lower threshold values (0.6-0.8) make more of the scene bloom, creating a dreamy/stylized look. Higher values (1.0-1.5) only bloom very bright areas, creating a realistic look. Adjust based on your scene's HDR range.

Screen-Space Ambient Occlusion (SSAO)#

SSAO adds contact shadows in crevices and corners, improving depth perception and realism. Essential for architectural visualization and realistic rendering.

SSAO Properties#

PropertyDescriptionRecommended Range
ssaoIntensityDarkness of ambient occlusion0.0 - 1.0
ssaoRadiusSampling radius in world units0.1 - 2.0
ssaoBiasPrevents self-shadowing artifacts0.001 - 0.1

Usage#

// Subtle SSAO (outdoor scenes)
PostProcessing.ssaoIntensity[postFx] = 0.3;
PostProcessing.ssaoRadius[postFx] = 0.5;
PostProcessing.ssaoBias[postFx] = 0.01;
// Strong SSAO (indoor scenes)
PostProcessing.ssaoIntensity[postFx] = 0.8;
PostProcessing.ssaoRadius[postFx] = 1.0;
PostProcessing.ssaoBias[postFx] = 0.02;
// Character close-ups
PostProcessing.ssaoIntensity[postFx] = 0.5;
PostProcessing.ssaoRadius[postFx] = 0.2; // Small radius for fine detail
PostProcessing.ssaoBias[postFx] = 0.005;
// Disable SSAO
PostProcessing.ssaoIntensity[postFx] = 0.0;

SSAO Performance

SSAO is moderately expensive. Use lower intensity (0.3-0.5) on mobile. Increase ssaoRadius for larger occlusion areas but expect performance cost. Adjust ssaoBias if you see dark halos around objects.

Depth of Field (DOF)#

Depth of field simulates camera focus, blurring objects outside the focal range. Great for cinematic shots, character portraits, and directing viewer attention.

DOF Properties#

PropertyDescriptionRecommended Range
dofFocusDistanceDistance to focus plane (meters)1.0 - 100.0
dofFocalLengthCamera focal length (mm)18 - 200
dofBokehScaleBokeh/blur size multiplier0.5 - 3.0

Usage#

// Portrait mode (shallow DOF)
PostProcessing.dofFocusDistance[postFx] = 5.0; // Focus at 5m
PostProcessing.dofFocalLength[postFx] = 85.0; // Portrait lens
PostProcessing.dofBokehScale[postFx] = 2.0; // Large bokeh
// Landscape (deep DOF)
PostProcessing.dofFocusDistance[postFx] = 50.0;
PostProcessing.dofFocalLength[postFx] = 24.0; // Wide angle
PostProcessing.dofBokehScale[postFx] = 0.5;
// Macro/close-up (very shallow)
PostProcessing.dofFocusDistance[postFx] = 1.5;
PostProcessing.dofFocalLength[postFx] = 100.0; // Macro lens
PostProcessing.dofBokehScale[postFx] = 3.0;
// Disable DOF (set bokehScale to 0)
PostProcessing.dofBokehScale[postFx] = 0.0;

Auto-Focus Example#

// Update focus distance to track target
const targetDistance = Vector3.distance(
cameraPosition,
targetPosition
);
PostProcessing.dofFocusDistance[postFx] = targetDistance;
// Smooth focus transition
const currentFocus = PostProcessing.dofFocusDistance[postFx];
const targetFocus = targetDistance;
PostProcessing.dofFocusDistance[postFx] =
currentFocus + (targetFocus - currentFocus) * 0.1; // Lerp

Tone Mapping and Exposure#

Tone mapping converts HDR (high dynamic range) colors to displayable LDR (low dynamic range). Exposure controls overall scene brightness.

Usage#

// Normal exposure
PostProcessing.toneMappingExposure[postFx] = 1.0;
// Bright/overexposed (daylight)
PostProcessing.toneMappingExposure[postFx] = 1.5;
// Dark/underexposed (night, caves)
PostProcessing.toneMappingExposure[postFx] = 0.6;
// High contrast (stylized)
PostProcessing.toneMappingExposure[postFx] = 1.8;

Color Grading (LUT)#

Color grading uses a lookup table (LUT) texture to apply cinematic color corrections:

// Load color grading LUT texture (16x16x16 or 32x32x32 3D LUT)
const lutAssetId = assetRegistry.loadTexture('luts/cinematic-teal-orange.png');
// Apply color grading
PostProcessing.colorGradingLUT[postFx] = lutAssetId;
// Common LUT styles:
// - Teal & Orange (blockbuster films)
// - Bleach bypass (desaturated, high contrast)
// - Warm vintage (golden hour)
// - Cool noir (blue shadows, high contrast)
// - Sepia (old photograph)
// Disable color grading
PostProcessing.colorGradingLUT[postFx] = 0;

Other Effects#

Vignette#

Darkens the edges of the screen, drawing focus to the center:

// Subtle vignette
PostProcessing.vignetteDarkness[postFx] = 0.3;
PostProcessing.vignetteOffset[postFx] = 1.0;
// Strong vignette (horror, suspense)
PostProcessing.vignetteDarkness[postFx] = 0.7;
PostProcessing.vignetteOffset[postFx] = 0.8;
// Large vignette (wide)
PostProcessing.vignetteDarkness[postFx] = 0.4;
PostProcessing.vignetteOffset[postFx] = 1.3;
// Disable vignette
PostProcessing.vignetteDarkness[postFx] = 0.0;

Chromatic Aberration#

Simulates lens color fringing, separating RGB channels at screen edges:

// Subtle aberration (realism)
PostProcessing.chromaticAberration[postFx] = 0.002;
// Strong aberration (damaged camera, VHS effect)
PostProcessing.chromaticAberration[postFx] = 0.01;
// Extreme aberration (glitch effect)
PostProcessing.chromaticAberration[postFx] = 0.05;
// Disable chromatic aberration
PostProcessing.chromaticAberration[postFx] = 0.0;

Motion Blur#

Adds blur based on camera and object motion for cinematic feel:

// Subtle motion blur
PostProcessing.motionBlurIntensity[postFx] = 0.3;
// Cinematic motion blur
PostProcessing.motionBlurIntensity[postFx] = 0.6;
// Strong motion blur (speed effect)
PostProcessing.motionBlurIntensity[postFx] = 1.0;
// Disable motion blur
PostProcessing.motionBlurIntensity[postFx] = 0.0;

Motion Blur Performance

Motion blur is expensive. Use sparingly on mobile devices. Recommended intensity: 0.3-0.5 for subtle effect without performance impact.

Screen-Space Reflections (SSR)#

// Enable SSR (expensive!)
PostProcessing.ssrEnabled[postFx] = 1;
// Disable SSR
PostProcessing.ssrEnabled[postFx] = 0;
// Note: SSR is very expensive and may not be suitable for all platforms
// Use for wet floors, mirrors, and reflective surfaces
// Consider using reflection probes instead for better performance

PostProcessVolume#

The PostProcessVolume component creates localized post-processing overrides that blend with global settings when the camera is inside the volume.

Properties#

PropertyTypeDefaultDescription
priorityui80Volume priority (higher = overrides lower priority volumes)
blendDistancef321.0Blend distance from volume edge (meters)
size[f32, 3][10, 10, 10]Volume box size [width, height, depth]

PostProcessVolume has the same effect properties as PostProcessing (bloom, SSAO, DOF, etc.) which override global settings when inside the volume.

Usage#

import { addComponent, PostProcessVolume, Transform } from '@web-engine/core';
// Create underwater post-process volume
const underwaterVolume = world.addEntity();
addComponent(world, Transform, underwaterVolume);
addComponent(world, PostProcessVolume, underwaterVolume);
// Position volume
Transform.position[underwaterVolume] = [0, -5, 0]; // Below water surface
// Set volume size
PostProcessVolume.size[underwaterVolume] = [100, 10, 100]; // Large underwater area
// Configure priority and blending
PostProcessVolume.priority[underwaterVolume] = 1;
PostProcessVolume.blendDistance[underwaterVolume] = 2.0; // 2m blend zone
// Override global settings with underwater effects
PostProcessVolume.bloomIntensity[underwaterVolume] = 0.2; // Less bloom
PostProcessVolume.vignetteDarkness[underwaterVolume] = 0.5; // Darker edges
PostProcessVolume.toneMappingExposure[underwaterVolume] = 0.7; // Darker overall
PostProcessVolume.chromaticAberration[underwaterVolume] = 0.005; // Slight distortion
// When camera enters volume, settings smoothly blend over blendDistance

Common Volume Use Cases#

// Cave entrance (dark interior)
const cave = world.addEntity();
addComponent(world, Transform, cave);
addComponent(world, PostProcessVolume, cave);
Transform.position[cave] = [50, 0, 0];
PostProcessVolume.size[cave] = [20, 10, 20];
PostProcessVolume.priority[cave] = 1;
PostProcessVolume.blendDistance[cave] = 5.0; // Gradual transition
PostProcessVolume.toneMappingExposure[cave] = 0.4; // Very dark
PostProcessVolume.ssaoIntensity[cave] = 0.8; // Strong shadows
PostProcessVolume.vignetteDarkness[cave] = 0.6;
// Toxic gas cloud (green tint, blur)
const toxic = world.addEntity();
addComponent(world, Transform, toxic);
addComponent(world, PostProcessVolume, toxic);
Transform.position[toxic] = [0, 2, 30];
PostProcessVolume.size[toxic] = [15, 5, 15];
PostProcessVolume.blendDistance[toxic] = 3.0;
PostProcessVolume.chromaticAberration[toxic] = 0.02; // Distortion
PostProcessVolume.vignetteDarkness[toxic] = 0.4;
// (Would also apply green color grading LUT)
// Dream sequence (high bloom, soft focus)
const dream = world.addEntity();
addComponent(world, Transform, dream);
addComponent(world, PostProcessVolume, dream);
Transform.position[dream] = [0, 0, 0];
PostProcessVolume.size[dream] = [50, 20, 50];
PostProcessVolume.priority[dream] = 2; // High priority
PostProcessVolume.bloomIntensity[dream] = 1.2;
PostProcessVolume.bloomThreshold[dream] = 0.5; // More bloom
PostProcessVolume.dofBokehScale[dream] = 2.0; // Soft focus
PostProcessVolume.toneMappingExposure[dream] = 1.3; // Brighter

Effect Execution Order#

Post-processing effects are applied in a specific order for optimal results:

OrderEffectPurpose
1SSAOAdd ambient occlusion to scene
2SSRAdd screen-space reflections
3Depth of FieldBlur out-of-focus areas
4Motion BlurAdd motion-based blur
5BloomAdd glow to bright areas
6Tone MappingMap HDR to LDR
7Color Grading (LUT)Apply color corrections
8VignetteDarken screen edges
9Chromatic AberrationAdd color fringing

Effect Pipeline

The order matters! For example, bloom is applied before tone mapping to work in HDR space. Vignette is applied after tone mapping to ensure proper darkness values. You cannot change the execution order.

Performance Considerations#

EffectCostMobile RecommendedNotes
BloomMedium0.3-0.5Reduce intensity and threshold on mobile
SSAOHigh0.0-0.3Very expensive, disable or minimize on mobile
DOFHigh0.0Expensive blur passes, disable on mobile
Motion BlurVery High0.0Multiple samples per pixel, avoid on mobile
SSRVery High0.0Ray marching, desktop only
Tone MappingLow1.0Always enabled, minimal cost
VignetteLow0.3Cheap overlay, safe for mobile
Chromatic AberrationLow0.002Simple offset, safe for mobile
Color GradingMediumEnabledTexture lookup, acceptable on mobile

Performance Presets#

// Low (Mobile)
PostProcessing.enabled[postFx] = 1;
PostProcessing.bloomIntensity[postFx] = 0.3;
PostProcessing.bloomThreshold[postFx] = 1.0;
PostProcessing.ssaoIntensity[postFx] = 0.0; // Disabled
PostProcessing.dofBokehScale[postFx] = 0.0; // Disabled
PostProcessing.motionBlurIntensity[postFx] = 0.0; // Disabled
PostProcessing.vignetteDarkness[postFx] = 0.2;
PostProcessing.toneMappingExposure[postFx] = 1.0;
// Medium (Desktop)
PostProcessing.bloomIntensity[postFx] = 0.5;
PostProcessing.ssaoIntensity[postFx] = 0.4;
PostProcessing.dofBokehScale[postFx] = 0.0; // Optional
PostProcessing.motionBlurIntensity[postFx] = 0.0; // Optional
PostProcessing.vignetteDarkness[postFx] = 0.3;
// High (High-end Desktop)
PostProcessing.bloomIntensity[postFx] = 0.8;
PostProcessing.ssaoIntensity[postFx] = 0.6;
PostProcessing.dofBokehScale[postFx] = 1.5;
PostProcessing.motionBlurIntensity[postFx] = 0.4;
PostProcessing.ssrEnabled[postFx] = 1;
PostProcessing.vignetteDarkness[postFx] = 0.4;
// Ultra (Cinematic)
PostProcessing.bloomIntensity[postFx] = 1.0;
PostProcessing.ssaoIntensity[postFx] = 0.8;
PostProcessing.dofBokehScale[postFx] = 2.5;
PostProcessing.motionBlurIntensity[postFx] = 0.6;
PostProcessing.ssrEnabled[postFx] = 1;

Best Practices#

  • Create one global PostProcessing entity per scene
  • Use PostProcessVolume for localized effects (underwater, caves, toxic gas)
  • Set volume priority appropriately (higher priority overrides lower)
  • Use blendDistance for smooth transitions between volumes
  • Disable expensive effects (SSAO, DOF, motion blur, SSR) on mobile
  • Keep bloom intensity moderate (0.3-0.6) for realistic look
  • Use SSAO for realism but monitor performance impact
  • Apply subtle vignette (0.2-0.4) to guide player focus
  • Use tone mapping exposure to balance scene brightness
  • Apply color grading LUTs for cinematic look
  • Test on target hardware before shipping with heavy effects
  • Provide graphics quality settings to let players adjust post-processing

Common Visual Presets#

Realistic/Photorealistic#

PostProcessing.bloomIntensity[postFx] = 0.3;
PostProcessing.bloomThreshold[postFx] = 1.0;
PostProcessing.ssaoIntensity[postFx] = 0.5;
PostProcessing.vignetteDarkness[postFx] = 0.2;
PostProcessing.toneMappingExposure[postFx] = 1.0;
PostProcessing.chromaticAberration[postFx] = 0.001;

Stylized/Cartoon#

PostProcessing.bloomIntensity[postFx] = 0.6;
PostProcessing.bloomThreshold[postFx] = 0.8;
PostProcessing.ssaoIntensity[postFx] = 0.2; // Less AO
PostProcessing.vignetteDarkness[postFx] = 0.1;
PostProcessing.toneMappingExposure[postFx] = 1.3; // Brighter
PostProcessing.chromaticAberration[postFx] = 0.0;

Horror/Dark#

PostProcessing.bloomIntensity[postFx] = 0.2;
PostProcessing.ssaoIntensity[postFx] = 0.8; // Heavy shadows
PostProcessing.vignetteDarkness[postFx] = 0.6; // Dark edges
PostProcessing.toneMappingExposure[postFx] = 0.6; // Underexposed
PostProcessing.chromaticAberration[postFx] = 0.005; // Slight distortion

Dreamy/Fantasy#

PostProcessing.bloomIntensity[postFx] = 1.0; // Heavy bloom
PostProcessing.bloomThreshold[postFx] = 0.6; // More bloom
PostProcessing.dofBokehScale[postFx] = 2.0; // Soft focus
PostProcessing.vignetteDarkness[postFx] = 0.3;
PostProcessing.toneMappingExposure[postFx] = 1.4; // Bright
PostProcessing.chromaticAberration[postFx] = 0.003;
Components | Web Engine Docs | Web Engine Docs