Home / Features / Post-processing
Bloom, TAA, GTAO, the sky LUTs and the debug views are not hard-wired stages. They are compute effects registered against a frame phase, and the twenty-line contract that carries them is the same one your own effect implements.
Effects live in Rendering/Effects/. Each one registers into a
compute phase, declares the textures it produces, and can be inspected as a 2D sprite by
name — which is how the reference application's debug mode works.
| Type | Phase | Purpose |
|---|---|---|
BloomEffect | AfterScene | Downsample / upsample chain over HDR scene colour. |
GtaoEffect | AfterScene | Ambient occlusion for indirect diffuse. |
TaaEffect | AfterScene | Jittered temporal resolve with variance clipping and sharpening. |
DdgiEffect | AfterScene | Probe tracing and irradiance atlas update. |
SkyAtmosphereEffect | FrameStart | Scattering LUTs, cloud noise and the aerial volume. |
SceneColorCopyEffect | AfterScene | A readable snapshot of scene colour for effects that need it. |
DepthViewEffect | AfterScene | Linearised depth as an inspectable texture. |
VelocityViewEffect | AfterScene | Motion vectors, visualised. |
Sdf3DViewEffect | FrameStart | Slice through a signed distance field of the kind DDGI traces. |
PlasmaEffect | FrameStart | A minimal compute example — the one to copy when writing your own. |
Any of them can be put on screen with three lines, because effect outputs are addressable textures:
AddControl(new Sprite2D
{
Name = Season.Rendering.Effects.GtaoEffect.TextureName,
Color = Colors.White,
PosX = 20, PosY = 580, Width = 240
});
Names follow one scheme: compute:// for 2D textures and
compute3d:// for volumes. GTAO publishes
compute://gtao/ao, the sky publishes
compute://sky/skyview and
compute3d://sky/aerial, and the plasma sample publishes
compute://plasma. Nothing about consuming them is special —
a Sprite2D or a material override takes the name and that is
the whole interface.
ComputeEffect is an abstract class with five members. That is
the entire extension surface.
public abstract class ComputeEffect
{
public abstract string Name { get; }
public abstract ComputePhase Phase { get; }
// Create textures and pipelines. Return false and the effect is
// dropped with nothing left behind.
public abstract bool Initialize(IGraphics g);
// Dispatch. Called once per frame, in phase order.
public abstract void Record(IGraphics g);
// Swapchain changed size. Recreate anything resolution-dependent.
public virtual void OnResize(IGraphics g) { }
}
Initialize returning false must leave no residue —
no half-created textures, no dangling names in
FrameSchedule. That is what makes an effect genuinely
optional: a backend without compute support and a user who switched the effect off
produce the same, correct frame.
Writing a compute effect walks through
PlasmaEffect line by line.
A threshold-and-knee soft clip followed by a six-level downsample and upsample chain over HDR scene colour. Because scene colour is genuinely HDR, the threshold means something: it selects pixels brighter than white rather than pixels that happened to clip.
| Setting | Default | Meaning |
|---|---|---|
BloomEnabled | on | Off skips the chain and clears FrameSchedule.BloomTexture. |
BloomThreshold | 1.0 | Radiance above which a pixel contributes. |
BloomKnee | 0.5 | Softness of the threshold, so the bloom fades in rather than switching on. |
BloomIntensity | 0.3 | How much of the chain is added back. |
BloomMipCount | 6 | Chain depth. More levels means a wider, softer glow. |
AaMode has four values and they are not four qualities of the
same thing — they operate in different colour spaces at different points in the frame.
| Mode | Where it runs | Notes |
|---|---|---|
Off | — | No filtering. |
Msaa4x | Raster | Legacy tier, D3D12 only. HDR resolve quality is compromised and bandwidth cost is high; it stays as a VR fallback. |
Fxaa | Post-tonemap LDR | FXAA 3.11, applied in the Post composite and at FinalBlit. Cheap and universal. |
Taa | HDR, before tonemap | Default. Needs velocity and compute; selecting it forces MotionVectors on at initialization. Falls back to Fxaa where unavailable. |
TAA is implemented and stabilised on D3D12, Vulkan, Metal and WebGPU. The fallback to FXAA is automatic rather than an error, which means a scene requesting TAA still renders on a device that cannot deliver it — softer, but rendered.
The camera jitters the projection matrix by a sub-pixel offset each frame, the resolve
reprojects the previous result through motion vectors, and variance clipping decides how much
of the history to trust. The interesting part is that both the jitter and the history live
on Camera3D, where you can read them.
| Setting | Default | Meaning |
|---|---|---|
MotionVectors | on | Whether the Scene pass writes velocity. TAA forces it. |
JitterPhaseCount | 7 | Length of the jitter sequence before it repeats. |
JitterScale | 1.0 | Jitter amplitude in pixels. Above 1 trades stability for coverage. |
TaaFeedback | 0.9 | History weight for moving pixels. |
TaaStaticFeedback | 0.97 | History weight where nothing moved — more history, more resolve. |
TaaVarianceClipGamma | 1.0 | How tightly history is clamped to the neighbourhood. Lower is more ghost-resistant and more aliased. |
TaaSharpness | 0.5 | Post-resolve sharpening, because temporal accumulation softens. |
On Camera3D | What it is for |
|---|---|
ProjectionJittered | The projection actually used to render, jitter included. |
PrevViewProjection | Last frame's matrix, which is what makes reprojection possible. |
JitterNdc, JitterPixels | This frame's offset, in both spaces. |
UpdateTemporal, ResetTemporal | Advance the sequence, or discard history after a camera cut. |
Reprojecting across a discontinuous camera move smears the old frame across the new one for as long as the feedback weight takes to decay. Cutting the camera is exactly the case motion vectors cannot describe.