4.X时代的色彩校正曲线和3D查找纹理技术,早期通过手动调节RGB通道曲线实现基础调色功能,而在URP中已整合为标准化体积框架下的模块化组件。
底层原理
Unity URP中的Color Adjustments后处理效果基于渲染管线中的片段着色器操作,其核心原理是通过线性空间下的数学变换调整像素颜色值。该效果在色调映射阶段前执行,作用于HDR颜色缓冲区的数据
曝光调整:通过pow(2, postExposure)对RGB通道进行指数级亮度缩放,模拟相机曝光补偿机制。计算过程发生在线性颜色空间,避免伽马校正干扰。
对比度计算:采用中间灰值(0.5)作为基准点,使用saturate((color.rgb - 0.5) * contrast + 0.5)公式扩展或压缩动态范围,其中saturate确保结果在[0,1]范围内。
色调/饱和度:将RGB转换到HSV色彩空间,调整H分量实现色相旋转,S分量控制颜色纯度,最后转回RGB空间.
实现示例
以下为URP内置着色器中颜色调整的核心代码逻辑:
曝光计算:采用2的幂次方实现物理正确的曝光模拟
HSV转换:通过几何插值法避免三角函数运算提升性能
动态范围保护:使用step()和saturate()防止数值溢出
ColorAdjustment.hlsl
// 线性空间下的颜色处理函数
half3 ApplyColorAdjustments(half3 color, float postExposure, float contrast, float hueShift, float saturation)
{
// 曝光调整
color *= exp2(postExposure);
// 对比度计算
color = (color - 0.5h) * contrast + 0.5h;
// RGB转HSV
half4 k = half4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
half4 p = lerp(half4(color.bg, k.wz), half4(color.gb, k.xy), step(color.b, color.g));
half4 q = lerp(half4(p.xyw, color.r), half4(color.r, p.yzx), step(p.x, color.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
half3 hsv = half3(abs(q.z + (q.w - q.y)/(6.0 * d + e)), d/(q.x + e), q.x);
// 色相/饱和度调整
hsv.x += hueShift;
hsv.y *= saturation;
// HSV转RGB
return HsvToRgb(hsv);
}
该效果在URP渲染流程中通过Volume系统注入参数,最终由FinalPost.shader在渲染目标上执行全屏绘制。色彩调整的运算顺序遵循:曝光→对比度→色调/饱和度→颜色滤镜,确保各阶段调整互不干扰.
核心参数与功能
Post Exposure:以EV单位调整场景整体曝光度,作用于HDR效果后、色调映射前,不影响处理链中其他效果。
Contrast:扩展(正值)或收缩(负值)色调范围,增强视觉层次感。
Color Filter:通过颜色选择器对渲染结果进行乘法着色,实现整体色调偏移。
Hue Shift:全局调整所有颜色的色相值,常用于风格化渲染。
Saturation:控制颜色强度,负值可生成灰度图像。
实现流程示例
创建后处理体积
添加空GameObject并挂载Volume组件
新建配置文件(如ColorProfile),通过Add Override > Post-processing添加Color Adjustments效果
相机配置
启用相机的Post Processing选项
在Volume Mask中匹配后处理体积的图层(如PostProcessLayer1)
参数调试
调整Contrast至1.2增强场景对比度
设置Color Filter为浅蓝色实现冷色调风格化
实际应用案例
恐怖游戏:降低饱和度至-30并微调色相,营造压抑氛围
开放世界:动态调整曝光度模拟昼夜光照变化
以下为完整URP实现代码示例:
脚本挂载至含Volume组件的对象
通过Inspector动态修改参数值
支持运行时效果切换
ColorAdjustmentSetup.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ColorAdjustmentSetup : MonoBehaviour
{
[SerializeField] private VolumeProfile profile;
[SerializeField] private float contrast = 1.2f;
[SerializeField] private Color colorFilter = new Color(0.8f, 0.9f, 1f);
void Start()
{
if (profile.TryGet(out ColorAdjustments adjustments))
{
adjustments.contrast.value = contrast;
adjustments.colorFilter.value = colorFilter;
}
}
}
该技术通过分离渲染管线与美术控制,实现了非破坏性工作流,成为现代游戏开发的标准实践