返回 Skills 目錄
unity-technologies/skills已通過檢查

SKILL DETAIL

urp-postprocessing

unity-technologies/skills/urp-postprocessing

This skill helps users set up, configure, and debug post-processing effects using URP's Volume framework. It covers the entire workflow from pre-flight checks to effect configuration and debugging, ensuring users achieve a working visual result with zero console errors. The skill includes Volume setup, configuration of various post-processing effects such as Bloom, Tonemapping, Color Adjustments, Depth of Field, Vignette, Motion Blur, and more, along with anti-hallucination rules and common recipes. It provides code snippets and a debugging checklist to help users avoid common mistakes and achieve desired effects.

安裝量 · 106查看來源

Installation

npx skills add https://github.com/unity-technologies/skills --skill urp-postprocessing

技能檔案

SKILL.md

最近同步 · 2026年9月13日

references/code-templates.md›
## Code Templates

Each template is a snippet to run through the Editor's `eval` command. Hard stops `throw`,
so the eval fails loudly; anything the caller needs to read is `return`ed.

**`eval` compiles a statement block, not a file.** `using` directives are rejected there — the
compiler reads `using UnityEngine;` as a resource-disposal statement and fails. So these
templates fully qualify every type. If you instead save the code as a `.cs` file for the user
to keep, add the usings back and drop the qualification.

### Creating a Global Volume with Effects

```csharp
// Verify HDR is enabled on the URP Asset
var urpAsset = UnityEngine.Rendering.Universal.UniversalRenderPipeline.asset;
if (urpAsset == null) { throw new System.Exception("No UniversalRenderPipelineAsset active."); }
if (!urpAsset.supportsHDR) { throw new System.Exception("HDR is disabled on the URP Asset. Enable it for Bloom/Tonemapping."); }

var profile = UnityEngine.ScriptableObject.CreateInstance<UnityEngine.Rendering.VolumeProfile>();
var assetPath = UnityEditor.AssetDatabase.GenerateUniqueAssetPath("Assets/Settings/PostProcessProfile.asset");
UnityEditor.AssetDatabase.CreateAsset(profile, assetPath);

// MUST set overrideState = true on each property
var bloom = profile.Add<UnityEngine.Rendering.Universal.Bloom>();
bloom.threshold.overrideState = true;
bloom.threshold.value = 0.9f;
bloom.intensity.overrideState = true;
bloom.intensity.value = 1f;
bloom.scatter.overrideState = true;
bloom.scatter.value = 0.7f;

var tonemapping = profile.Add<UnityEngine.Rendering.Universal.Tonemapping>();
tonemapping.mode.overrideState = true;
tonemapping.mode.value = UnityEngine.Rendering.Universal.TonemappingMode.ACES;

var volumeObj = new UnityEngine.GameObject("Global Volume");
var volume = volumeObj.AddComponent<UnityEngine.Rendering.Volume>();
volume.isGlobal = true;
volume.profile = profile;

UnityEditor.Undo.RegisterCreatedObjectUndo(volumeObj, "Create Global Volume");
UnityEditor.EditorUtility.SetDirty(profile);
UnityEditor.AssetDatabase.SaveAssets();

return "Created Global Volume with Bloom and ACES Tonemapping.";
```

### Enabling Post-Processing on Camera

```csharp
var cam = UnityEngine.Camera.main;
if (cam == null) { throw new System.Exception("No Main Camera found."); }

if (!cam.TryGetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>(out var data)) { throw new System.Exception("Missing UniversalAdditionalCameraData. Is URP active?"); }

UnityEditor.Undo.RecordObject(data, "Enable Post-Processing");
data.renderPostProcessing = true;
UnityEditor.EditorUtility.SetDirty(data);

// This edits a component, which lives in the scene rather than in an asset, so
// AssetDatabase.SaveAssets() does not persist it. SetDirty only marks; nothing is written until
// the scene is saved. Mark the scene and say it is unsaved, rather than reporting the change as
// done: if the session ends without a scene save, the edit is gone.
var scene = data.gameObject.scene;
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(scene);
return $"Post-processing enabled on '{cam.name}'. Scene '{scene.name}' is modified but NOT saved. "
     + "Save it, or the change is lost when the Editor session ends.";
```

The right choice depends on whether anyone is watching:

- **Interactive run** (the user is at the Editor): report and let them save. A save from here also
  commits whatever else they had in progress in that scene, which is not yours to decide.
- **Non-interactive run** (batch, CI, or a session that is about to end): call
  `UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene)` and say that you saved it.
  Nobody is there to act on a "scene is unsaved" report, so leaving it unsaved just loses the work.

### Modifying an Existing Volume Profile

**Use `sharedProfile`, not `profile`.** `profile` auto-clones the asset into a runtime instance, so
`SetDirty` on it marks a clone that is not an asset at all and the edit can never reach disk. Merely
reading `volume.profile` is enough to trigger the clone, so the null check has to use `sharedProfile`
too. `sharedProfile` returns the asset itself, which is what an Editor-time edit needs.

```csharp
var volumeObj = UnityEngine.GameObject.Find("Global Volume");
if (volumeObj != null && volumeObj.TryGetComponent<UnityEngine.Rendering.Volume>(out var volume)
    && volume.sharedProfile != null)
{
    var profile = volume.sharedProfile;
    if (profile.TryGet<UnityEngine.Rendering.Universal.Bloom>(out var bloom))
    {
        bloom.intensity.overrideState = true;
        bloom.intensity.value = 2f;
    }
    UnityEditor.EditorUtility.SetDirty(profile);
    // SetDirty only marks the asset. Without this the edit is in memory only: it reads back
    // correctly for the rest of the session and is lost when the session ends.
    UnityEditor.AssetDatabase.SaveAssets();
}
```

### Making a change undoable through `eval`

`Undo.RecordObject` alone does **not** produce an undo entry when the snippet runs through the
Editor's `eval` command (measured: nothing appeared on the undo stack). `RecordObject` takes a
*deferred* snapshot that Unity flushes at the end of an Editor event, and a snippet executed by
the Pipeline server is outside that loop.

Use the explicit group + immediate-snapshot + flush sequence instead, which does not rely on
the event loop:

```csharp
UnityEditor.Undo.IncrementCurrentGroup();
UnityEditor.Undo.SetCurrentGroupName("Set up post-processing");     // the label the user will see
var group = UnityEditor.Undo.GetCurrentGroup();

UnityEditor.Undo.RegisterCompleteObjectUndo(target, "Set up post-processing");   // immediate, not deferred
// ... make the modification, and for a newly created object:
// UnityEditor.Undo.RegisterCreatedObjectUndo(newObject, "Set up post-processing");
UnityEditor.EditorUtility.SetDirty(target);

// SetDirty marks; it does not write. Persist according to what `target` is, or the change is
// lost at session end even though every read-back looks correct:
//   an asset (Volume Profile, URP Asset)  -> UnityEditor.AssetDatabase.SaveAssets();
//   a component in a scene                -> MarkSceneDirty(target.gameObject.scene) and tell
//                                            the user the scene needs saving
UnityEditor.Undo.FlushUndoRecordObjects();         // force the snapshot out now
UnityEditor.Undo.CollapseUndoOperations(group);    // one entry, not several

return UnityEditor.Undo.GetCurrentGroupName();     // report the label back to confirm it landed
```

**Verify it landed rather than assuming.** The returned group name tells you the group exists;
to confirm the entry is actually on the stack, have the user check `Edit > Undo <label>` in the
menu — it should read your label.

**If it still doesn't land, fall back to a scoped revert rather than dropping the guarantee.**
Before modifying, read the current value of the field you're about to change and report it with
a one-line snippet that restores it, so reverting is a single command:

```csharp
// captured before the change
var previous = data.renderPostProcessing;         // report: previous = false
// revert snippet to hand the user:
//   data.renderPostProcessing = false;
```

Scene and prefab files are text-serialized, so version control is the last resort, not the
first answer — offer the scoped revert before pointing at `git`.
references/effect-reference.md›
## Effect Reference

**Bloom** (`Bloom`) — Glow on bright areas. Best with HDR rendering enabled on the URP Asset; works in SDR if `threshold` < 1.
- `threshold` (MinFloatParameter, default 0.9, min 0) — brightness cutoff below which pixels don't bloom.
- `intensity` (MinFloatParameter, default 0, min 0) — strength of the bloom effect.
- `scatter` (ClampedFloatParameter, default 0.7, 0–1) — spread/diffusion of the glow.
- `clamp` (MinFloatParameter, default 65472, min 0) — clamps pixel intensity to prevent artifacts.
- `tint` (ColorParameter, default white) — color tint applied to bloom.
- `highQualityFiltering` (BoolParameter, default false) — uses bicubic upsampling for smoother bloom.
- `downscale` (BloomDownscaleMode: `Half`, `Quarter`) — resolution for bloom computation.
- `maxIterations` (ClampedIntParameter, default 6, 2–8) — number of blur passes.

**Tonemapping** (`Tonemapping`) — Maps HDR to display range. Requires HDR.
- `mode` (TonemappingMode: `None`, `Neutral`, `ACES`) — `ACES` for cinematic, `Neutral` for minimal color shift.

**Color Adjustments** (`ColorAdjustments`) — **WARNING: Class is `ColorAdjustments`, NOT `ColorGrading`.**
- `postExposure` (FloatParameter, default 0) — exposure adjustment in EV units. Unbounded.
- `contrast` (ClampedFloatParameter, default 0, -100–100).
- `colorFilter` (ColorParameter, default white) — multiplied against the final color.
- `hueShift` (ClampedFloatParameter, default 0, -180–180) — shifts all hues.
- `saturation` (ClampedFloatParameter, default 0, -100–100).

**Depth of Field** (`DepthOfField`)
- `mode` (DepthOfFieldMode: `Off`, `Gaussian`, `Bokeh`).
- Gaussian mode: `gaussianStart` (MinFloatParameter, default 10, min 0), `gaussianEnd` (MinFloatParameter, default 30, min 0), `gaussianMaxRadius` (ClampedFloatParameter, default 1, 0.5–1.5).
- Bokeh mode: `focusDistance` (MinFloatParameter, default 10, min 0.1), `focalLength` (ClampedFloatParameter, default 50, 1–300), `aperture` (ClampedFloatParameter, default 5.6, 1–32), `bladeCount` (ClampedIntParameter, default 5, 3–9), `bladeCurvature` (ClampedFloatParameter, default 1, 0–1), `bladeRotation` (ClampedFloatParameter, default 0, -180–180).

**Vignette** (`Vignette`)
- `intensity` (ClampedFloatParameter, default 0, 0–1).
- `smoothness` (ClampedFloatParameter, default 0.2, 0.01–1).
- `color` (ColorParameter, default black).
- `center` (Vector2Parameter, default (0.5, 0.5)).
- `rounded` (BoolParameter, default false) — constrains vignette to circular shape.

**Motion Blur** (`MotionBlur`)
- `mode` (MotionBlurMode: `CameraOnly`, `CameraAndObjects`). `CameraAndObjects` requires per-object motion vectors.
- `quality` (MotionBlurQuality: `Low`, `Medium`, `High`).
- `intensity` (ClampedFloatParameter, default 0, 0–1).
- `clamp` (ClampedFloatParameter, default 0.05, 0–0.2) — maximum velocity in screen fraction.

**Film Grain** (`FilmGrain`)
- `type` (FilmGrainLookup: `Thin1`, `Thin2`, `Medium1`–`Medium6`, `Large01`, `Large02`, `Custom`).
- `intensity` (ClampedFloatParameter, default 0, 0–1).
- `response` (ClampedFloatParameter, default 0.8, 0–1).
- `texture` (NoInterpTextureParameter, default null) — required when `type` is `Custom`.

**Chromatic Aberration** (`ChromaticAberration`)
- `intensity` (ClampedFloatParameter, default 0, 0–1). Keep subtle (0.05–0.15) for realism.

**Split Toning** (`SplitToning`)
- `shadows` (ColorParameter, default grey).
- `highlights` (ColorParameter, default grey).
- `balance` (ClampedFloatParameter, default 0, -100–100).

**Lens Distortion** (`LensDistortion`)
- `intensity` (ClampedFloatParameter, default 0, -1–1). Negative = barrel, positive = pincushion.
- `xMultiplier` (ClampedFloatParameter, default 1, 0–1).
- `yMultiplier` (ClampedFloatParameter, default 1, 0–1).
- `center` (Vector2Parameter, default (0.5, 0.5)).
- `scale` (ClampedFloatParameter, default 1, 0.01–5).

**White Balance** (`WhiteBalance`)
- `temperature` (ClampedFloatParameter, default 0, -100–100). Negative = cool, positive = warm.
- `tint` (ClampedFloatParameter, default 0, -100–100). Negative = green, positive = magenta.

**Panini Projection** (`PaniniProjection`) — Only useful with camera FOV > 90.
- `distance` (ClampedFloatParameter, default 0, 0–1).
- `cropToFit` (ClampedFloatParameter, default 1, 0–1).

**Lift Gamma Gain** (`LiftGammaGain`) — Each is `Vector4Parameter` (RGBA where A is intensity offset, defaults to (1,1,1,0)).
- `lift` — affects shadows.
- `gamma` — affects midtones.
- `gain` — affects highlights.

**Shadows Midtones Highlights** (`ShadowsMidtonesHighlights`) — Each `Vector4Parameter`, defaults to (1,1,1,0).
- `shadows`, `midtones`, `highlights`.
- Zone boundaries: `shadowsStart` (default 0), `shadowsEnd` (default 0.3), `highlightsStart` (default 0.55), `highlightsEnd` (default 1). All `MinFloatParameter`, min 0.

**Color Curves** (`ColorCurves`) — All `TextureCurveParameter`.
- `master`, `red`, `green`, `blue` — default linear (0,0)→(1,1).
- `hueVsHue`, `hueVsSat`, `satVsSat`, `lumVsSat` — default empty, neutral at 0.5.

**Channel Mixer** (`ChannelMixer`) — All `ClampedFloatParameter`, -200–200.
- `redOutRedIn` (default 100), `redOutGreenIn` (default 0), `redOutBlueIn` (default 0).
- `greenOutRedIn` (default 0), `greenOutGreenIn` (default 100), `greenOutBlueIn` (default 0).
- `blueOutRedIn` (default 0), `blueOutGreenIn` (default 0), `blueOutBlueIn` (default 100).
SKILL.md›
---
name: urp-postprocessing
description: Sets up, configures, and debugs URP post-processing effects using the Volume framework. Use when the user asks about bloom, tonemapping, color adjustments, depth of field, vignette, motion blur, or other Volume overrides in a URP project.
required_packages:
  com.unity.render-pipelines.universal: ">=14.0.0"
---

Help the user set up, configure, and debug post-processing effects using URP's Volume framework.

**Goal: The user should have a working visual result with zero console errors after setup.**

## 0. Prerequisite: an Editor you can run C# in

Volume profiles, `VolumeParameter.overrideState`, and the camera's post-processing flags are
Editor/runtime object state — the checks and edits below all run C# inside a live Editor.

**The `unity-cli` skill owns getting you there** — installing the CLI, confirming a connected
Editor, adding the project's `com.unity.pipeline` package, telling a genuinely absent Editor
apart from one stuck in Safe Mode, and discovering the Editor's command catalog. Follow it
first; don't re-derive any of it here. You need `eval` in particular, not just a reachable
Editor: its presence depends on the Pipeline package version, not on the CLI. If it's
missing, say so and stop.

Run C# through the connected Editor with the `eval` command. Discover its parameter shape
from `unity command --format json` rather than assuming one — the inline form is
`unity command eval --code '<snippet>'`, and some Pipeline versions also register
`eval_file` for running a snippet from a file. **Check the catalog before reaching for
`eval_file`; it is frequently absent.** `unity command` defaults to a 30 second timeout.

### Passing C# to `eval`

`eval` compiles a **statement block, not a file**. Two consequences, both of which cause a
compile error rather than a warning:

- **No `using` directives.** The compiler reads `using UnityEngine;` as a resource-disposal
  statement and rejects it (`CS0210`).
- **Types must be fully qualified.** A bare `AssetDatabase` or `Volume` does not resolve
  (`CS0246` / `CS0103`), and a bare `Object` is ambiguous with `object` (`CS0104`).

Where a snippet below is written as a file — with usings, for readability, or because it is
meant to be saved into the project — qualify the types before passing it to `eval`.

## 0. Pre-Flight Checks

Before configuring any effect, **verify all checks**. Fix failures first.

1. **URP is the active render pipeline** — If not, inform the user and stop.
2. **HDR is enabled on the URP Asset** — Required for Tonemapping. Bloom works best with HDR; in SDR it still works but `threshold` must be < 1.
3. **Camera has post-processing enabled** — `renderPostProcessing` must be `true` (defaults to `false`). Camera Stacking: only a `CameraRenderType.Base` camera (or the last `Overlay` in the stack) should enable post-processing. Also verify the Renderer's PostProcessData asset is not null — if it is, the post-process pass won't exist.
4. **The Volume's GameObject layer is in the Camera's Volume Layer Mask** — `volumeLayerMask` defaults to layer 0 "Default" only. The Volume's `GameObject.layer` must be included, otherwise the camera ignores it.
5. **Volume exists with `enabled = true`, a valid Profile, and at least one override** — The `Volume` component must be enabled, have a non-null `profile` (or `sharedProfile`), and at least one `VolumeComponent` with `overrideState = true` on its properties.

### Pre-Flight Check Snippet

Run this to verify the setup programmatically:

```csharp
// `eval` compiles a statement block, not a file: no `using` directives are
// allowed, so every type is fully qualified.
var report = new System.Text.StringBuilder();

// 1. Check URP is active — a hard stop, so throw: it fails the eval loudly
var urpAsset = UnityEngine.Rendering.Universal.UniversalRenderPipeline.asset;
if (urpAsset == null)
    throw new System.Exception("URP is not the active render pipeline.");

// 2. Check HDR
if (!urpAsset.supportsHDR)
    report.AppendLine("Warning: HDR is disabled on the URP Asset. Tonemapping won't work; Bloom requires threshold < 1.");

// 3. Check camera post-processing
var cam = UnityEngine.Camera.main;
if (cam == null)
    throw new System.Exception("No Main Camera found.");
if (!cam.TryGetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>(out var camData))
    throw new System.Exception("Missing UniversalAdditionalCameraData on camera. Is URP active?");
if (!camData.renderPostProcessing)
    report.AppendLine("Warning: Post-processing is disabled on the camera. Enable via camData.renderPostProcessing = true.");

// 4. Check volume layer mask
var volumes = UnityEngine.Object.FindObjectsByType<UnityEngine.Rendering.Volume>(UnityEngine.FindObjectsSortMode.None);
foreach (var vol in volumes)
{
    if (!vol.enabled) { report.AppendLine($"Warning: Volume '{vol.name}' is disabled."); continue; }
    if ((camData.volumeLayerMask & (1 << vol.gameObject.layer)) == 0)
        report.AppendLine($"Warning: Volume '{vol.name}' on layer {vol.gameObject.layer} is not in camera's volumeLayerMask.");
    // 5. Check profile and overrides
    var profile = vol.sharedProfile;
    if (profile == null) { report.AppendLine($"Warning: Volume '{vol.name}' has no profile assigned."); continue; }
    if (profile.components.Count == 0)
        report.AppendLine($"Warning: Volume '{vol.name}' profile has no overrides.");
}

// Return the findings: logs land in the Editor console, the returned value comes back to you
return report.Length == 0 ? "Post-processing setup looks correct." : report.ToString();
```

## 1. Volume Setup

Effects are added as **VolumeComponent overrides** on a **VolumeProfile** (a `ScriptableObject`).

**Global Volume** (most common): GameObject with `Volume` component, `isGlobal = true`, `profile` assigned. Affects every camera whose `volumeLayerMask` includes the Volume's layer.

**Local Volume (optional, but takes precedence)**: GameObject with trigger `Collider` + `Volume` component, `isGlobal = false`. Properties:
- `priority` (float) — higher values override lower when volumes overlap.
- `blendDistance` (float) — outer distance in world units to start blending from (0 = no blend, instant transition at collider boundary).
- `weight` (float, 0–1) — scales the volume's overall influence.

## 2. Post-Processing Effects

All effects are `VolumeComponent` subclasses added as overrides on a `VolumeProfile` via `profile.Add<T>()`. Check existence with `profile.Has<T>()` or `profile.TryGet<T>(out var t)`. Remove with `profile.Remove<T>()`.

Every property is a `VolumeParameter`. You **must** set `overrideState = true` before setting `value`, otherwise the Volume system ignores it.

When configuring a specific effect, load the full API reference:
- [references/effect-reference.md](references/effect-reference.md) — All VolumeComponent properties by effect (Bloom, Tonemapping, ColorAdjustments, DepthOfField, Vignette, MotionBlur, FilmGrain, ChromaticAberration, SplitToning, LensDistortion, WhiteBalance, PaniniProjection, LiftGammaGain, ShadowsMidtonesHighlights, ColorCurves, ChannelMixer)

For code templates:
- [references/code-templates.md](references/code-templates.md) — Global Volume setup, camera post-processing, and profile modification templates

## 3. Anti-Hallucination Rules

### Required Usings

These apply when you write a `.cs` file into the project. **A snippet passed to `eval` cannot
carry them** — qualify the types instead (see "Passing C# to `eval`" above).

```csharp
using UnityEngine.Rendering;           // Volume, VolumeProfile, VolumeComponent, VolumeParameter
using UnityEngine.Rendering.Universal;  // Bloom, Tonemapping, ColorAdjustments, UniversalRenderPipeline, etc.
```

### Wrong → Correct API Mapping

| WRONG | CORRECT |
|-------|---------|
| `PostProcessVolume` | `Volume` (from `UnityEngine.Rendering`) |
| `PostProcessLayer` | `UniversalAdditionalCameraData.renderPostProcessing` (bool) |
| `UnityEngine.Rendering.PostProcessing` | `UnityEngine.Rendering.Universal` |
| `profile.GetSetting<T>()` | `profile.TryGet<T>(out var t)` (returns bool) |
| `profile.AddSettings<T>()` | `profile.Add<T>()` (returns T; throws if already exists — check `profile.Has<T>()` first) |
| `volume.sharedProfile` (to modify at runtime) | `volume.profile` (auto-clones the asset into an instance) |
| `VolumeManager.instance.stack.GetComponent<T>()` | `volume.profile.TryGet<T>(out var t)` |

### Key Facts
- **`overrideState = true`** is required on every `VolumeParameter` you set. The volume system skips parameters where `overrideState` is `false`. This is the #1 scripting mistake.
- **`sharedProfile`** = returns the asset directly (edits persist to disk). **`profile`** = auto-clones into an instance if needed (safe for runtime edits). Check with `volume.HasInstantiatedProfile()`.
- **`profile.Add<T>(bool overrides = false)`** — pass `true` to auto-enable `overrideState` on all parameters of the added component.

## 4. Debugging Checklist

When post-processing isn't working, check in order:

1. `cam.TryGetComponent<UniversalAdditionalCameraData>(out var data)` succeeds and `data.renderPostProcessing` is `true`?
2. Volume exists in scene with a non-null `profile` (or `sharedProfile`) assigned?
3. Overrides added via `profile.Add<T>()` AND `overrideState = true` on each property you set?
4. Volume's `GameObject.layer` is included in camera's `data.volumeLayerMask`? (Default mask is layer 0 "Default" only.)
5. `volume.isGlobal = true` (for global), or camera is inside the Volume's trigger `Collider` (for local)?
6. Camera `data.renderType` is `CameraRenderType.Base`, not `Overlay`? (Overlay cameras composite onto the Base camera's output.)
7. `UniversalRenderPipeline.asset.supportsHDR` is `true`? Required for Bloom and Tonemapping.
8. Viewing in **Game view**? Scene view has a separate post-processing toggle in its toolbar.

## 5. Common Recipes

Format: Effect property=value. Bloom values are threshold/intensity/scatter.

**Cinematic (Film):** Tonemapping mode=ACES, ColorAdjustments contrast=15 saturation=-10, Bloom threshold=0.9 intensity=0.5 scatter=0.7, Vignette intensity=0.3 smoothness=0.4, FilmGrain type=Medium1 intensity=0.2

**Stylized/Vibrant:** Tonemapping mode=Neutral, ColorAdjustments saturation=20 contrast=10, Bloom threshold=0.8 intensity=1.5 scatter=0.6, SplitToning highlights=warm shadows=cool

**Horror/Dark:** ColorAdjustments postExposure=-0.5 saturation=-30 contrast=20, Vignette intensity=0.5 smoothness=0.3 color=dark-red, FilmGrain type=Large01 intensity=0.4, ChromaticAberration intensity=0.15

**Clean/Mobile:** Tonemapping mode=Neutral, ColorAdjustments postExposure=0.2, Bloom threshold=1.0 intensity=0.3 (subtle). Avoid FilmGrain, MotionBlur, DepthOfField on mobile.

## 6. Final Confirmation

After setup, report to user:

```
Post-Processing Setup Complete
- Volume: [Global/Local] on "[GameObject Name]"
- Profile: [Asset Path]
- Effects: [List with key property=value pairs]
- Camera: [Name] — renderPostProcessing=true, volumeLayerMask includes layer [N]

View results in Game view (not Scene view).
Undo all changes with Edit > Undo (Ctrl+Z).
```