Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
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.
The PostProcessing component defines global post-processing settings that apply to the entire scene. Only one PostProcessing component should exist per scene.
| Property | Type | Default | Description |
|---|---|---|---|
| enabled | ui8 | 1 | Enable post-processing pipeline (0=off, 1=on) |
| bloomIntensity | f32 | 0.0 | Bloom effect intensity (0=off, 1.0=strong) |
| bloomThreshold | f32 | 1.0 | Brightness threshold for bloom |
| bloomSmoothing | f32 | 0.5 | Bloom smoothness/radius (0-1) |
| vignetteDarkness | f32 | 0.0 | Vignette darkness (0=off, 1=black edges) |
| vignetteOffset | f32 | 1.0 | Vignette offset/size (0.5=large, 1.5=small) |
| chromaticAberration | f32 | 0.0 | Chromatic aberration offset (0=off, 0.01=subtle) |
| toneMappingExposure | f32 | 1.0 | Tone mapping exposure (0.5=dark, 2.0=bright) |
| ssaoIntensity | f32 | 0.0 | SSAO effect intensity (0=off, 1.0=strong) |
| ssaoRadius | f32 | 0.5 | SSAO sampling radius in world units |
| ssaoBias | f32 | 0.01 | SSAO bias to prevent self-shadowing artifacts |
| colorGradingLUT | ui32 | 0 | Color grading LUT texture asset ID (0=none) |
| ssrEnabled | ui8 | 0 | Screen-space reflections (0=off, 1=on) |
| dofFocusDistance | f32 | 10.0 | Depth of field focus distance (meters) |
| dofFocalLength | f32 | 50.0 | Depth of field focal length (mm, 18-200) |
| dofBokehScale | f32 | 1.0 | Depth of field bokeh size |
| motionBlurIntensity | f32 | 0.0 | Motion blur intensity (0=off, 1.0=strong) |
import { addComponent, PostProcessing } from '@web-engine/core';// Create global post-processing entityconst postFx = world.addEntity();addComponent(world, PostProcessing, postFx);// Enable post-processingPostProcessing.enabled[postFx] = 1;// Configure basic effectsPostProcessing.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 creates a glow around bright areas, simulating camera lens bloom and light scattering. Perfect for bright lights, magic effects, and sci-fi visuals.
| Property | Description | Recommended Range |
|---|---|---|
| bloomIntensity | Overall bloom strength | 0.0 - 1.0 |
| bloomThreshold | Brightness required to bloom (in HDR) | 0.8 - 1.2 |
| bloomSmoothing | Bloom spread/radius | 0.3 - 0.8 |
// 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 glowPostProcessing.bloomIntensity[postFx] = 0.8;PostProcessing.bloomThreshold[postFx] = 0.6;PostProcessing.bloomSmoothing[postFx] = 0.7;// Disable bloomPostProcessing.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.
SSAO adds contact shadows in crevices and corners, improving depth perception and realism. Essential for architectural visualization and realistic rendering.
| Property | Description | Recommended Range |
|---|---|---|
| ssaoIntensity | Darkness of ambient occlusion | 0.0 - 1.0 |
| ssaoRadius | Sampling radius in world units | 0.1 - 2.0 |
| ssaoBias | Prevents self-shadowing artifacts | 0.001 - 0.1 |
// 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-upsPostProcessing.ssaoIntensity[postFx] = 0.5;PostProcessing.ssaoRadius[postFx] = 0.2; // Small radius for fine detailPostProcessing.ssaoBias[postFx] = 0.005;// Disable SSAOPostProcessing.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 simulates camera focus, blurring objects outside the focal range. Great for cinematic shots, character portraits, and directing viewer attention.
| Property | Description | Recommended Range |
|---|---|---|
| dofFocusDistance | Distance to focus plane (meters) | 1.0 - 100.0 |
| dofFocalLength | Camera focal length (mm) | 18 - 200 |
| dofBokehScale | Bokeh/blur size multiplier | 0.5 - 3.0 |
// Portrait mode (shallow DOF)PostProcessing.dofFocusDistance[postFx] = 5.0; // Focus at 5mPostProcessing.dofFocalLength[postFx] = 85.0; // Portrait lensPostProcessing.dofBokehScale[postFx] = 2.0; // Large bokeh// Landscape (deep DOF)PostProcessing.dofFocusDistance[postFx] = 50.0;PostProcessing.dofFocalLength[postFx] = 24.0; // Wide anglePostProcessing.dofBokehScale[postFx] = 0.5;// Macro/close-up (very shallow)PostProcessing.dofFocusDistance[postFx] = 1.5;PostProcessing.dofFocalLength[postFx] = 100.0; // Macro lensPostProcessing.dofBokehScale[postFx] = 3.0;// Disable DOF (set bokehScale to 0)PostProcessing.dofBokehScale[postFx] = 0.0;
// Update focus distance to track targetconst targetDistance = Vector3.distance(cameraPosition,targetPosition);PostProcessing.dofFocusDistance[postFx] = targetDistance;// Smooth focus transitionconst currentFocus = PostProcessing.dofFocusDistance[postFx];const targetFocus = targetDistance;PostProcessing.dofFocusDistance[postFx] =currentFocus + (targetFocus - currentFocus) * 0.1; // Lerp
Tone mapping converts HDR (high dynamic range) colors to displayable LDR (low dynamic range). Exposure controls overall scene brightness.
// Normal exposurePostProcessing.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 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 gradingPostProcessing.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 gradingPostProcessing.colorGradingLUT[postFx] = 0;
Darkens the edges of the screen, drawing focus to the center:
// Subtle vignettePostProcessing.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 vignettePostProcessing.vignetteDarkness[postFx] = 0.0;
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 aberrationPostProcessing.chromaticAberration[postFx] = 0.0;
Adds blur based on camera and object motion for cinematic feel:
// Subtle motion blurPostProcessing.motionBlurIntensity[postFx] = 0.3;// Cinematic motion blurPostProcessing.motionBlurIntensity[postFx] = 0.6;// Strong motion blur (speed effect)PostProcessing.motionBlurIntensity[postFx] = 1.0;// Disable motion blurPostProcessing.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.
// Enable SSR (expensive!)PostProcessing.ssrEnabled[postFx] = 1;// Disable SSRPostProcessing.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
The PostProcessVolume component creates localized post-processing overrides that blend with global settings when the camera is inside the volume.
| Property | Type | Default | Description |
|---|---|---|---|
| priority | ui8 | 0 | Volume priority (higher = overrides lower priority volumes) |
| blendDistance | f32 | 1.0 | Blend 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.
import { addComponent, PostProcessVolume, Transform } from '@web-engine/core';// Create underwater post-process volumeconst underwaterVolume = world.addEntity();addComponent(world, Transform, underwaterVolume);addComponent(world, PostProcessVolume, underwaterVolume);// Position volumeTransform.position[underwaterVolume] = [0, -5, 0]; // Below water surface// Set volume sizePostProcessVolume.size[underwaterVolume] = [100, 10, 100]; // Large underwater area// Configure priority and blendingPostProcessVolume.priority[underwaterVolume] = 1;PostProcessVolume.blendDistance[underwaterVolume] = 2.0; // 2m blend zone// Override global settings with underwater effectsPostProcessVolume.bloomIntensity[underwaterVolume] = 0.2; // Less bloomPostProcessVolume.vignetteDarkness[underwaterVolume] = 0.5; // Darker edgesPostProcessVolume.toneMappingExposure[underwaterVolume] = 0.7; // Darker overallPostProcessVolume.chromaticAberration[underwaterVolume] = 0.005; // Slight distortion// When camera enters volume, settings smoothly blend over blendDistance
// 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 transitionPostProcessVolume.toneMappingExposure[cave] = 0.4; // Very darkPostProcessVolume.ssaoIntensity[cave] = 0.8; // Strong shadowsPostProcessVolume.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; // DistortionPostProcessVolume.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 priorityPostProcessVolume.bloomIntensity[dream] = 1.2;PostProcessVolume.bloomThreshold[dream] = 0.5; // More bloomPostProcessVolume.dofBokehScale[dream] = 2.0; // Soft focusPostProcessVolume.toneMappingExposure[dream] = 1.3; // Brighter
Post-processing effects are applied in a specific order for optimal results:
| Order | Effect | Purpose |
|---|---|---|
| 1 | SSAO | Add ambient occlusion to scene |
| 2 | SSR | Add screen-space reflections |
| 3 | Depth of Field | Blur out-of-focus areas |
| 4 | Motion Blur | Add motion-based blur |
| 5 | Bloom | Add glow to bright areas |
| 6 | Tone Mapping | Map HDR to LDR |
| 7 | Color Grading (LUT) | Apply color corrections |
| 8 | Vignette | Darken screen edges |
| 9 | Chromatic Aberration | Add 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.
| Effect | Cost | Mobile Recommended | Notes |
|---|---|---|---|
| Bloom | Medium | 0.3-0.5 | Reduce intensity and threshold on mobile |
| SSAO | High | 0.0-0.3 | Very expensive, disable or minimize on mobile |
| DOF | High | 0.0 | Expensive blur passes, disable on mobile |
| Motion Blur | Very High | 0.0 | Multiple samples per pixel, avoid on mobile |
| SSR | Very High | 0.0 | Ray marching, desktop only |
| Tone Mapping | Low | 1.0 | Always enabled, minimal cost |
| Vignette | Low | 0.3 | Cheap overlay, safe for mobile |
| Chromatic Aberration | Low | 0.002 | Simple offset, safe for mobile |
| Color Grading | Medium | Enabled | Texture lookup, acceptable on mobile |
// Low (Mobile)PostProcessing.enabled[postFx] = 1;PostProcessing.bloomIntensity[postFx] = 0.3;PostProcessing.bloomThreshold[postFx] = 1.0;PostProcessing.ssaoIntensity[postFx] = 0.0; // DisabledPostProcessing.dofBokehScale[postFx] = 0.0; // DisabledPostProcessing.motionBlurIntensity[postFx] = 0.0; // DisabledPostProcessing.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; // OptionalPostProcessing.motionBlurIntensity[postFx] = 0.0; // OptionalPostProcessing.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;
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;
PostProcessing.bloomIntensity[postFx] = 0.6;PostProcessing.bloomThreshold[postFx] = 0.8;PostProcessing.ssaoIntensity[postFx] = 0.2; // Less AOPostProcessing.vignetteDarkness[postFx] = 0.1;PostProcessing.toneMappingExposure[postFx] = 1.3; // BrighterPostProcessing.chromaticAberration[postFx] = 0.0;
PostProcessing.bloomIntensity[postFx] = 0.2;PostProcessing.ssaoIntensity[postFx] = 0.8; // Heavy shadowsPostProcessing.vignetteDarkness[postFx] = 0.6; // Dark edgesPostProcessing.toneMappingExposure[postFx] = 0.6; // UnderexposedPostProcessing.chromaticAberration[postFx] = 0.005; // Slight distortion
PostProcessing.bloomIntensity[postFx] = 1.0; // Heavy bloomPostProcessing.bloomThreshold[postFx] = 0.6; // More bloomPostProcessing.dofBokehScale[postFx] = 2.0; // Soft focusPostProcessing.vignetteDarkness[postFx] = 0.3;PostProcessing.toneMappingExposure[postFx] = 1.4; // BrightPostProcessing.chromaticAberration[postFx] = 0.003;