概述
后处理(Post-Processing)是在屏幕渲染完成后,对最终图像进行再处理的阶段。URP 通过 Volume 系统管理后处理效果,支持 分层配置(全局/区域/相机)和 运行时混合。
入门系列中没有涉及后处理。本篇从 URP Volume 系统的基础配置开始,覆盖常用效果的调参指南,然后扩展到自定义后处理效果的实现。
1. URP Volume 系统
1.1 三层 Volume
| 层级 |
用途 |
配置方式 |
| Global Volume |
全局默认效果 |
场景中放一个 Global Volume,覆盖整个场景 |
| Local Volume |
区域特效 |
带 Collider 的 Volume,玩家进入后触发 |
| Camera Volume |
相机独享 |
挂载在相机上的 Volume,不共享 |
1.2 启用后处理
1.3 Profile 管理
Volume Profile 存储所有后处理效果的参数配置。
1 2 3 4 5 6 7
| [SerializeField] private Volume _globalVolume; [SerializeField] private VolumeProfile _underwaterProfile;
public void EnterUnderwater() { _globalVolume.profile = _underwaterProfile; }
|
2. 常用效果详解
2.1 色调映射(Tonemapping)
控制亮部和高光的映射方式,解决 HDR 颜色到 LDR 屏幕的转换:
| 模式 |
效果 |
推荐场景 |
| None |
无调整 |
低配移动端 |
| Neutral |
中性映射,保真度高 |
写实风格 |
| ACES |
电影级色调,对比度高 |
电影感、高画质 |
| Reinhard |
高亮柔和 |
卡通渲染 |
2.2 泛光(Bloom)
让亮部像素溢出到周围区域,产生发光感:
1 2 3 4
| Intensity: 0.3 - 1.5 Threshold: 0.8 - 1.2 Scatter: 0.3 - 0.7 Tint: 默认白色
|
移动端 Bloom 是 GPU 开销最大的后处理之一,Intensity 不超过 1.0。
2.3 环境光遮蔽(Ambient Occlusion)
在物体接触位置添加阴影,增强立体感:
1 2 3
| Intensity: 0.5 - 1.0 Radius: 0.5 - 2.0 Quality: Medium(移动端建议 Medium)
|
2.4 色彩调整
1 2 3 4
| Color Adjustments -> Post-exposure: -1 到 1(整体亮度) Contrast: -50 到 50 Saturation: -50 到 50
|
3. 自定义后处理效果
3.1 Volume 组件
1 2 3 4 5 6 7 8 9 10 11
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
[System.Serializable, VolumeComponentMenu("Custom/Scanline")] public class ScanlineVolumeComponent : VolumeComponent { public FloatParameter intensity = new(0f); public FloatParameter density = new(50f); public ColorParameter tint = new(Color.black); }
|
3.2 渲染 Pass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ScanlineRenderPass : ScriptableRenderPass { private readonly Material _material; private readonly ScanlineVolumeComponent _volume; private RenderTargetIdentifier _cameraColorTarget; private RenderTextureDescriptor _descriptor;
public ScanlineRenderPass(Material material, ScanlineVolumeComponent volume) { _material = material; _volume = volume; renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; }
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { _cameraColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; _descriptor = renderingData.cameraData.cameraTargetDescriptor; }
public override void Execute(ScalableBufferManager context, ref RenderingData renderingData, CommandBuffer cmd) { if (_volume.intensity.value <= 0) return;
_material.SetFloat("_Intensity", _volume.intensity.value); _material.SetFloat("_Density", _volume.density.value); _material.SetColor("_Tint", _volume.tint.value);
var tempTex = Shader.PropertyToID("_TempScanlineTex"); cmd.GetTemporaryRT(tempTex, _descriptor); cmd.Blit(_cameraColorTarget, tempTex); cmd.Blit(tempTex, _cameraColorTarget, _material); cmd.ReleaseTemporaryRT(tempTex); } }
|
3.3 Renderer Feature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class ScanlineFeature : ScriptableRendererFeature { [SerializeField] private Shader _shader; private Material _material; private ScanlineRenderPass _pass;
public override void Create() { _material = new Material(_shader); _pass = new ScanlineRenderPass(_material, VolumeManager.instance.stack .GetComponent<ScanlineVolumeComponent>()); }
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (renderingData.cameraData.cameraType == CameraType.Game) renderer.EnqueuePass(_pass); }
protected override void Dispose(bool disposing) { if (disposing) CoreUtils.Destroy(_material); } }
|
4. 运行时控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class PostProcessingController : MonoBehaviour { private Volume _volume; private Vignette _vignette;
private void Awake() { _volume = GetComponent<Volume>(); _volume.profile.TryGet(out _vignette); }
public async UniTask DamageFlashAsync() { _vignette.intensity.value = 0.5f; _vignette.color.value = Color.red; await UniTask.Delay(200); _vignette.intensity.value = 0f; } }
|
总结
- URP 的 Volume 系统 支持全局/区域/相机三层配置
- Volume Component + Scriptable Render Pass + Shader 三步创建自定义效果
- 运行时通过脚本控制参数实现平滑过渡(受伤闪红、水下切换)
- 移动端后处理需要精准控制,只保留必要的效果
下一篇将讲解 渲染优化,涵盖 CPU/GPU 两端的性能瓶颈及解决方案。