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#
| 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) |
Basic Usage#
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 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#
| 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 |
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 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.
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#
| 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 |
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-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 (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#
| 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 |
Usage#
// 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;Auto-Focus Example#
// 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; // LerpTone Mapping and Exposure#
Tone mapping converts HDR (high dynamic range) colors to displayable LDR (low dynamic range). Exposure controls overall scene brightness.
Usage#
// 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 (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 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;Other Effects#
Vignette#
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;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 aberrationPostProcessing.chromaticAberration[postFx] = 0.0;Motion Blur#
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.
Screen-Space Reflections (SSR)#
// 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 performancePostProcessVolume#
The PostProcessVolume component creates localized post-processing overrides that blend with global settings when the camera is inside the volume.
Properties#
| 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.
Usage#
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 blendDistanceCommon 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 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 priority PostProcessVolume.bloomIntensity[dream] = 1.2;PostProcessVolume.bloomThreshold[dream] = 0.5; // More bloomPostProcessVolume.dofBokehScale[dream] = 2.0; // Soft focusPostProcessVolume.toneMappingExposure[dream] = 1.3; // BrighterEffect Execution Order#
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.
Performance Considerations#
| 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 |
Performance Presets#
// 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;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 AOPostProcessing.vignetteDarkness[postFx] = 0.1;PostProcessing.toneMappingExposure[postFx] = 1.3; // BrighterPostProcessing.chromaticAberration[postFx] = 0.0;Horror/Dark#
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 distortionDreamy/Fantasy#
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;