the symptom, in your words

"The engine ships render quality profiles that cap your scene — and post-processing is opt-in per camera"

✓ verified on 26.07.22
lane Getting art inposted

▸ SYMPTOM

  • A scene's authored graphics settings don't render as authored: fewer shadow cascades, lower shadow resolution, no ambient occlusion, no bloom, no tonemapping — even though every value was set correctly.
  • A camera created in code renders flat, ungraded, un-occluded, non-blooming lighting no matter how carefully the lights are placed.
  • A performance number measured on one machine doesn't reproduce on another, because the two machines are at different engine quality levels.

▸ CAUSE

Two separate engine facts combine here.

1. The engine ships render quality profiles that cap your scene. core/cfg/quality_profiles.json defines four groups — TextureQuality, PostProcessQuality, VolumetricFogQuality, ShadowQuality — each with Low/Medium/High, applied through Sandbox.Engine.Settings.RenderQualityProfiles.SetGroupConVars(group, level). These are a ceiling, not a starting point:

  • DirectionalLight.ShadowCascadeCount is a request, not a setting. Its own XML doc says "User settings will set a maximum," and ShadowQuality Low pins r.shadows.csm.maxcascades 2. A scene authoring 4 cascades renders 2 for that player. The same shape applies to r.shadows.maxresolution (512/1024/2048), r.shadows.csm.maxresolution (1024/2048/4096) and r.shadows.quality (1/2/3).
  • So any measured cascade or shadow cost is the cost at the measuring machine's engine setting, not a portable constant.

2. Post-processing is opt-in per camera — the convars tune passes that don't exist until you add the component. r_ao_quality, r_dof_quality, r_motionblur_quality and friends are real and settable, and setting them alone does nothing. The GTAO pass only runs when a Sandbox.AmbientOcclusion component sits on the camera. A scene whose cameras are created in code with AddComponent<CameraComponent>() and never given Tonemapping / Bloom / AmbientOcclusion renders with none of them.

The full opt-in component set is Sandbox.{AmbientOcclusion, Bloom, Blur, ChromaticAberration, ColorGrading, DepthOfField, FilmGrain, MotionBlur, Pixelate, ScreenSpaceReflections, Sharpen, Tonemapping, Vignette}, all in Sandbox.Engine.xml, with gtao_cs.shader, postprocess_bloom.shader, screen_space_reflections_cs.shader and tonemapping/ all shipping compiled in addons/base/Assets/shaders/.

Screen-space reflections follow the same opt-in rule — and this is a common trap. A non-zero r_ssr_downsample_ratio (it reads 2 on a stock machine) does not mean SSR is running. SSR needs a Sandbox.ScreenSpaceReflections component on the camera; the convar only tunes a pass that already exists. Measured across r_ssr_downsample_ratio 0, 2, and full-resolution 1 with a world rebuild per setting, frames came back identical within noise and with no mirrored geometry at any setting, because no SSR component was attached. The general rule: a non-zero engine convar is necessary but not sufficient — the component is what decides whether the pass runs.

▸ FIX

  • Add the post-process components you want, per camera. If a camera is built in code, attach Tonemapping, Bloom, AmbientOcclusion, ScreenSpaceReflections, etc. explicitly. The convars only shape passes that already exist.
  • Treat shadow cascade/resolution settings as requests that the player's quality tier can cap. Don't benchmark shadow cost as a portable constant; measure it at a known engine setting and note that setting.
  • Restore any quality convars you write. These are client-global console state, not scene tuning. A game preset that writes them and does not restore them leaves the next performance measurement grading a configuration nobody pinned.

▸ WHY IT WORKS

Post-processing in this engine is a per-camera opt-in stack, and the quality profiles are a global ceiling applied on top of whatever the scene requested. Once you understand both, the "correct settings, wrong picture" mystery resolves: the picture is flat because no post-process component is attached, and the shadows are coarser than authored because the player's quality tier capped them. Attaching the components makes the passes exist; measuring at a pinned quality tier makes the numbers portable.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord