【从UnityURP开始探索游戏渲染】专栏-直达
色调映射(Tonemapping)是Unity URP后处理中的关键技术,用于将高动态范围(HDR)图像适配到标准动态范围(SDR)显示设备,解决亮度范围超出显示器能力导致的细节丢失问题。以下是综合解析:
作用
核心功能
- 动态范围压缩:将HDR光照数据(如阳光直射与阴影的极端亮度差)映射到0-1的LDR范围,避免高光过曝或暗部细节丢失。
- 视觉优化:通过非线性曲线调整亮度和对比度,模拟人眼对光照的非线性感知,增强画面电影感或自然感。
- 典型应用:HDR渲染、Bloom特效配合、影视化色彩分级。
与Gamma校正的区别
- Gamma校正是简单的幂律变换,而色调映射涉及全局/局部的动态范围适配策略,如ACES算法会改变色相和饱和度以实现电影级效果。
发展历史
- 早期算法:如Reinhard算子(全局映射),通过对数压缩保留整体氛围,但局部对比度不足。
- 进阶算法:ACES(学院色彩编码系统)成为行业标准,提供更自然的亮部压缩和色彩空间转换。
- 现代优化:Unity URP/HDRP引入Custom模式,支持用户自定义曲线参数(如Toe/Shoulder强度),平衡性能与效果。
原理
色调映射(Tonemapping)是将高动态范围(HDR)图像转换为标准动态范围(SDR)显示的核心技术,其底层原理主要涉及非线性压缩和感知优化。以下是详细解释:
HDR到LDR的转换需求
在HDR渲染中,光照强度可能远超显示器能表现的0-1范围(如阳光亮度可达6.5单位),直接显示会导致亮部细节丢失为纯白。色调映射通过以下方式解决:
- 动态范围压缩:将HDR的高亮度值(如>1.0)非线性压缩到LDR的0-1范围,避免简单截断导致的亮部细节丢失。
- 感知适配:模拟人眼对亮度的非线性响应(如韦伯-费希纳定律),在暗部保留更多细节。
核心算法原理
ACES曲线(常用电影级算法)
-
公式:通过有理分式实现高光柔和压缩,同时增强中间调对比度。
f(x)=\\frac {x(2.51x+0.03)}{x(2.43x+0.59)+0.14}
-
示例效果:输入亮度6.5会被映射到约0.95,而0.5亮度映射到0.45,既保留高光层次又避免整体发灰。
Neutral模式(中性映射)
-
采用对数变换:最小化色相偏移,适合需要后续色彩分级的场景。
f(x)=\\frac {log(x+1)}{log(X_{max}+1)}
自定义曲线参数
- Toe/Shoulder控制 :
- Toe Strength调整暗部过渡(0.5时暗部细节更明显)
- Shoulder Angle控制高光压缩斜率(值越大高光保留越多)(具体参数见下表)
| 参数 | 作用 | 典型值 |
|---|---|---|
| Gamma | 整体伽马校正 | 2.2 |
| Toe Length | 暗部动态范围占比 | 0.3-0.5 |
| Shoulder Strength | 高光过渡硬度 | 0.5-0.8 |
URP实现示例
在URP中通过Volume组件添加Tonemapping覆盖,关键代码如下:
csharp
csharp
// 通过Volume API动态修改参数
var volume = GetComponent<Volume>();
if (volume.profile.TryGet(out Tonemapping tone)) {
tone.mode.value = TonemappingMode.ACES;
tone.shoulderStrength.value = 0.7f;
}
此代码将模式设为ACES并调整高光过渡强度。
视觉对比实验
测试场景中:
- 无Tonemapping时,阳光区域(亮度5.0)显示为全白;
- 启用ACES后,同一区域呈现为淡黄色并保留云层纹理。
该技术本质是基于人眼感知特性的动态范围重映射,通过非线性函数平衡物理准确性与视觉舒适度。
Unity URP实现流程
启用Tonemapping
需通过Volume框架添加后处理覆盖:
- 创建或选择已有Volume GameObject。
- 在Inspector中点击
Add Override > Post-processing > Tonemapping。
参数详解
| 参数 | 说明 | 用例 |
|---|---|---|
| Mode | 映射算法类型 | ACES适合电影感,Neutral保留原始色彩 |
| Toe Strength | 暗部过渡强度 | 值越高,阴影对比度越强(Custom模式有效) |
| Shoulder Length | 高光动态范围 | 控制亮部细节保留程度 |
| Lookup Texture | 自定义LUT纹理 | 实现风格化调色(如赛博朋克色调) |
完整示例代码
以下为URP中自定义Tonemapping的Shader实现示例:
代码说明:
-
Shader实现ACES算法,通过曝光参数(
_Exposure)控制亮度映射。 -
Volume脚本提供运行时参数调整,集成到URP后处理堆栈。
-
TonemappingEffect.shader
cShader "PostProcessing/Tonemapping" { Properties {...} SubShader { Pass { // ACES近似算法核心 float3 ACESTonemap(float3 color) { float a = 2.51, b = 0.03, c = 2.43, d = 0.59, e = 0.14; return saturate((color*(a*color+b))/(color*(c*color+d)+e)); } fixed4 frag(v2f i) : SV_Target { float4 src = tex2D(_MainTex, i.uv); float3 tonemapped = ACESTonemap(src.rgb * _Exposure); return float4(tonemapped, src.a); } } } } -
TonemappingVolume.cs
csharpusing UnityEngine.Rendering; public class TonemappingVolume : VolumeComponent { public TonemappingModeParameter mode = new TonemappingModeParameter(TonemappingMode.ACES); public FloatParameter exposure = new FloatParameter(1.0f); }
实际用例建议
- 开放世界游戏 :使用
ACES模式增强日落时的高光自然过渡。 - 移动端优化 :改用
Neutral模式减少计算开销,或简化ACES算法(如拟合矩阵)。 - 艺术风格化 :结合
Lookup Texture实现像素风或复古胶片效果。
通过调整参数组合(如Toe Length+Shoulder Angle),可精细控制画面动态范围分布
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)