【從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開始探索遊戲渲染】專欄-直達
(歡迎點贊留言探討,更多人加入進來能更加完善這個探索的過程,🙏)