Adding Post-Processing Effects to the Pascal Editor 3D Viewer
The Pascal Editor implements post-processing effects through an isolated WebGPU render pipeline in the viewer package, using Three.js TSL nodes for SSGI, outlines, TRAA, and denoising that developers can configure via exported parameters and integrate into any React application.
The Pascal Editor (pascalorg/editor) renders 3D scenes using a dedicated viewer package that exposes a WebGPU-based post-processing stack. This architecture deliberately separates rendering logic from the editor UI, allowing developers to add visual effects using Three Shader Language (TSL) nodes without modifying core application code.
How the WebGPU RenderPipeline Works
Inside packages/viewer/src/components/viewer/post-processing.tsx, the system constructs a WebGPU RenderPipeline that processes frames through a node-based shader graph. The pipeline rebuilds automatically whenever the project ID changes or when the application explicitly requests a retry, ensuring post-processing effects stay synchronized with the current 3D scene.
The implementation imports specialized TSL display nodes—outline, ssgi, traa, and denoise—and connects them through standard TSL operations like add, mix, and output to produce the final composed image. This node graph approach allows you to chain effects declaratively without writing raw WGSL shaders.
Configuring SSGI and Effect Parameters
Global illumination settings are tunable through the exported SSGI_PARAMS constant defined in the post-processing module. These parameters control screen-space global illumination quality, radius, and intensity without requiring pipeline recompilation.
Key configuration options include:
enabled– Toggle SSGI on or offradius– Sampling radius for indirect lighting (default is 2.0 for softer GI)giIntensity– Boost factor for indirect lighting contribution- Slice count – Determines ray marching quality
// Enable SSGI and customise parameters
import { SSGI_PARAMS } from '@pascal-app/viewer/src/components/viewer/post-processing';
SSGI_PARAMS.enabled = true;
SSGI_PARAMS.radius = 2.0; // Larger radius for softer GI
SSGI_PARAMS.giIntensity = 0.8; // Boost indirect lighting
Layer Masking for Selective Effects
The pipeline uses strict layer masking to ensure post-processing affects only specific geometry. While the main scene renders to SCENE_LAYER, the post-processing pass targets ZONE_LAYER exclusively. This separation—configured in packages/viewer/src/lib/layers.ts—prevents UI elements or gizmos from receiving unwanted blur, bloom, or ambient occlusion.
When setting up custom geometry that requires post-processing, ensure you assign it to ZONE_LAYER so the RenderPipeline includes it in the effect chain:
import { ZONE_LAYER } from '@pascal-app/viewer/src/lib/layers';
mesh.layers.set(ZONE_LAYER);
Handling Background Colors and Theme Transitions
To prevent visual flashes on first render, the component maintains a bgUniform that stores the current scene background color. Each frame, this uniform lerps toward the active theme color (dark or light), creating smooth transitions when users switch themes. The PostProcessing component reads the theme from the viewer store and updates bgUniform automatically, ensuring the clear color matches the UI without manual intervention.
Retry Logic and Error Resilience
Pipeline creation includes defensive retry mechanics. If WebGPU context loss or shader compilation fails, the system attempts recreation up to MAX_PIPELINE_RETRIES with a delay of RETRY_DELAY_MS between attempts. Errors are logged to the console, but rendering continues in a degraded state rather than crashing the application, maintaining user agency even on unsupported hardware.
Integrating Post-Processing in Your Application
The PostProcessing component is imported and rendered inside the main Viewer component at packages/viewer/src/components/viewer/index.tsx. This encapsulation keeps effect logic completely separate from the editor app, allowing you to drop the viewer into any React project with full post-processing support.
Basic implementation requires only the Viewer component and a project ID:
import Viewer from '@pascal-app/viewer/src/components/viewer';
import { useTheme } from 'some-ui-lib';
export default function ProjectViewer({ projectId }) {
const theme = useTheme(); // 'dark' | 'light'
return (
<Viewer
projectId={projectId}
theme={theme} // Background colour updates automatically
/>
);
}
Triggering pipeline rebuilds (e.g., after changing settings) uses the viewer store:
import useViewer from '@pascal-app/viewer/src/store/use-viewer';
function RefreshButton() {
const requestRebuild = useViewer(state => state.requestPipelineRebuild);
return <button onClick={requestRebuild}>Rebuild Post‑FX</button>;
}
Summary
- Pipeline location:
packages/viewer/src/components/viewer/post-processing.tsxcontains the WebGPU RenderPipeline and TSL node graph. - Effect nodes: Available TSL nodes include
outline,ssgi,traa, anddenoise, chained viaadd,mix, andoutput. - Configuration: Modify
SSGI_PARAMSto adjust global illumination without rebuilding the pipeline. - Layer isolation: Only geometry on
ZONE_LAYERreceives post-processing; the main scene usesSCENE_LAYER. - Theme support: Background colors transition smoothly via
bgUniformlerping to prevent flash on load. - Error handling: Automatic retry with
MAX_PIPELINE_RETRIESensures robustness against WebGPU context loss. - Integration: Import the
Viewercomponent frompackages/viewer/src/components/viewer/index.tsxand useuseViewerfor state management.
Frequently Asked Questions
What TSL nodes are available for post-processing in the Pascal Editor?
The viewer provides four primary TSL display nodes: outline for edge detection, ssgi for screen-space global illumination, traa for temporal anti-aliasing, and denoise for noise reduction. These are imported into post-processing.tsx and connected through a node graph using standard TSL operations like add and mix to create the final composite output.
How do I enable or disable SSGI in the 3D viewer?
Import the SSGI_PARAMS constant from @pascal-app/viewer/src/components/viewer/post-processing and toggle the enabled boolean property. You can also adjust radius and giIntensity numerically to control the softness and strength of indirect lighting. Changes take effect immediately without requiring a pipeline rebuild.
Why are post-processing effects applied only to certain layers?
The architecture uses layer masking to maintain UI clarity. The pipeline renders only objects on ZONE_LAYER through the post-processing stack, while SCENE_LAYER handles standard rendering. This ensures that helper objects, gizmos, or overlay UI remain sharp and unaffected by blur or glow effects applied to the main 3D content.
How does the viewer handle theme changes without flashing?
The component creates a bgUniform that stores the current background color. On each frame, it lerps this uniform toward the active theme color (provided via the theme prop on the Viewer component). This gradual interpolation prevents the jarring color jump that would otherwise occur on the first render or during theme switches.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →