【从UnityURP开始探索游戏渲染】专栏-直达
Split Toning(色调分离)是Unity URP后处理中用于对图像阴影和高光区域分别进行颜色校正的效果,通过独立控制阴影和高光的色调来创造独特的视觉风格。以下是详细解析:
核心功能与用途
- 视觉风格化:将阴影和高光区域分离着色,常见于电影调色(如《银翼杀手2049》的橙青色调)或游戏场景氛围营造
- 色彩对比增强:通过互补色强化画面层次感,例如阴影用冷色(蓝)、高光用暖色(橙)
- 性能优势:属于低开销的色彩校正类后处理,适合移动端使用
发展历史
- 起源自传统胶片摄影的化学调色工艺,后引入数字图像处理
- Unity早期通过Amplify Color等插件实现,2018年后整合到Post Processing Stack v2中,现为URP/HDRP标准组件
原理
SplitToning是Unity URP后处理中用于实现色调分离效果的技术,其核心原理是通过对图像的高光和阴影区域分别应用不同的颜色映射,从而创造出艺术化的色彩分级效果。以下是详细解析:
底层原理
- 颜色分离机制
- SplitToning将图像像素按亮度分为高光(亮部)和阴影(暗部)两部分,通过阈值控制分离范围。高光区域应用
_SplitToningHighlightsColor
,阴影区域应用_SplitToningShadowsColor
,中间过渡区域通过平滑插值混合。
- SplitToning将图像像素按亮度分为高光(亮部)和阴影(暗部)两部分,通过阈值控制分离范围。高光区域应用
- LUT(颜色查找表)支持
- URP可能结合LUT技术加速颜色映射。LUT将输入颜色值映射到预定义输出值,SplitToning的色调映射可通过LUT贴图(如1024x32尺寸)高效实现,每个Tile对应不同的亮度区间。
- Shader实现流程
- 采样原始图像像素并计算亮度(如使用
Luminance()
函数) - 根据亮度值选择高光或阴影颜色
- 应用平滑过渡(如
smoothstep
函数)避免硬边界 - 最终输出混合结果。
- 采样原始图像像素并计算亮度(如使用
示例说明
以下是一个简化的Shader代码片段,展示SplitToning的核心逻辑:
hlsl
float3 ApplySplitToning(float3 inputColor, float3 shadowsColor, float3 highlightsColor, float balance) {
float luminance = Luminance(inputColor);
float t = smoothstep(0.2, 0.8, luminance); // 过渡区间控制
float3 shadows = lerp(inputColor, shadowsColor * inputColor, 1.0 - t);
float3 highlights = lerp(inputColor, highlightsColor * inputColor, t);
return lerp(shadows, highlights, balance); // 平衡参数调节整体倾向
}
- 参数说明:
shadowsColor
/highlightsColor
:阴影/高光的目标色调(如蓝色高光+橙色阴影)balance
:控制整体偏向高光或阴影(0.5为均衡)。
性能优化建议
-
与URP管线集成
通过
RenderFeature
将SplitToning作为后处理阶段插入渲染管线,注意在Volume
组件中配置参数以实现动态调整。 -
结合SRP Batcher
若自定义Shader,需确保符合SRP Batcher要求(如使用
CBUFFER
封装变量),以减少DrawCall开销。 -
LUT优化
使用256x16的小尺寸LUT贴图可降低带宽占用,但可能损失精度;1024x32适合高质量需求。
-
典型应用场景包括电影感调色(如《银翼杀手》风格的冷色调高光+暖色调阴影)或风格化游戏渲染
参数详解与用例
参数 | 含义 | 典型用例 |
---|---|---|
Shadows | 阴影区域色调(RGB) | 暗部填充冷色(如#1E3A8A)增强景深 |
Highlights | 高光区域色调(RGB) | 亮部使用暖色(如#F59E0B)模拟阳光 |
Balance | 阴影/高光混合权重 | 正值偏向高光,负值强化阴影(-20~20) |
URP实现流程
-
SplitToningExample.cs
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class SplitToningExample : VolumeComponent, IPostProcessComponent { [Tooltip("阴影色调")] public ColorParameter shadows = new ColorParameter(Color.blue); [Tooltip("高光色调")] public ColorParameter highlights = new ColorParameter(Color.yellow); [Tooltip("平衡值")] public ClampedFloatParameter balance = new ClampedFloatParameter(0f, -20f, 20f); public bool IsActive() => shadows.value != Color.gray || highlights.value != Color.gray; public bool IsTileCompatible() => false; }
-
SplitToningRenderPass.cs
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class SplitToningRenderPass : ScriptableRenderPass { private Material material; private SplitToningExample settings; public SplitToningRenderPass(Material mat) { material = mat; renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; } public void Setup(SplitToningExample volSettings) { settings = volSettings; if (settings != null) material.SetColor("_Shadows", settings.shadows.value); material.SetColor("_Highlights", settings.highlights.value); material.SetFloat("_Balance", settings.balance.value); } public override void Execute(ScriptableRenderContext context, ref RenderingData data) { CommandBuffer cmd = CommandBufferPool.Get("SplitToning"); Blit(cmd, ref data, material, 0); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } }
-
SplitToningFeature.cs
using UnityEngine; using UnityEngine.Rendering.Universal; public class SplitToningFeature : ScriptableRendererFeature { private SplitToningRenderPass pass; public Material effectMaterial; public override void Create() { pass = new SplitToningRenderPass(effectMaterial); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) { var stack = VolumeManager.instance.stack; var settings = stack.GetComponent<SplitToningExample>(); if (settings.IsActive()) { pass.Setup(settings); renderer.EnqueuePass(pass); } } }
使用步骤
-
创建Volume Profile
- Hierarchy右键 → Volume → Global Volume
- 添加
SplitToningExample
组件
-
Shader实现
hlsl // Shader核心算法 half3 ApplySplitToning(half3 color, half3 shadows, half3 highlights, half balance) { half luminance = Luminance(color); half t = saturate(luminance - balance * 0.01); return lerp(shadows, highlights, t) * color; }
-
效果调试
- 阴影色调:适用于地下城/夜晚场景(#2E1065)
- 高光色调:适合沙漠/黄昏(#F97316)
- Balance:-10使画面更阴沉,+10增强阳光感
性能优化建议
- 避免与Bloom等高开销效果叠加使用
- 移动端建议使用LUT(颜色查找表)替代实时计算
- 通过Local Volume按需启用(如仅在过场动画使用)
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)